@elberpg/ex-date-picker
v2.0.2
Published
Composable scroll-based date & time picker with modal shells for React Native / Expo
Maintainers
Readme
@elberpg/ex-date-picker
Composable date and time pickers for React Native / Expo.
Drum-scroll columns — no native dependencies, no @elberpg/ex-modal required.
Spanish docs: README_ES.md
Preview
| Default — Date | Default — Time |
|:-:|:-:|
|
|
|
Fully themeable via global style objects — no props required.
| Dark ☕ — Date | Dark ☕ — Time | Square ⬛ — Date | Square ⬛ — Time |
|:-:|:-:|:-:|:-:|
|
|
|
|
|
Installation
npm install @elberpg/ex-date-pickerConcept — composable pattern
Each picker and modal are independent components. You compose them:
<DatePickerModal visible={open} onClose={onClose} onConfirm={() => onConfirm(draft)}>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>This gives full style control over each part independently.
DatePicker
Scroll columns for day, month, and year. Use standalone or inside any modal.
import { DatePicker } from '@elberpg/ex-date-picker';
<DatePicker
value={date}
onChange={setDate}
locale="en-US"
/>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | Date \| DatePickerValue | today | Current value |
| onChange | (date: Date) => void | — | Called on every scroll change |
| locale | string | 'en' | Locale for month names |
| minYear | number | current − 100 | Minimum year |
| maxYear | number | current + 10 | Maximum year |
| itemHeight | number | from theme | Row height in px |
| selectedColor | string | from theme | Color of the selected item |
| unselectedColor | string | from theme | Color of unselected items |
| style | ViewStyle | — | Container style |
TimePicker
Scroll columns for hour and minute. Supports 12-hour (AM/PM) mode.
import { TimePicker } from '@elberpg/ex-date-picker';
<TimePicker
value={time}
onChange={setTime}
use12h={false}
/>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | TimeValue \| Date | now | Current value |
| onChange | (time: TimeValue) => void | — | Called on every scroll change |
| use12h | boolean | false | 12-hour mode with AM/PM column |
| itemHeight | number | from theme | Row height in px |
| selectedColor | string | from theme | Color of the selected item |
| unselectedColor | string | from theme | Color of unselected items |
| style | ViewStyle | — | Container style |
TimeValue
interface TimeValue {
hours: number; // 0–23 (or 1–12 in 12h mode)
minutes: number; // 0–59
}DatePickerModal — native RN Modal
Bottom-sheet using React Native's Modal component. Renders on top of everything including toasts.
import { DatePicker, DatePickerModal } from '@elberpg/ex-date-picker';
const [open, setOpen] = useState(false);
const [date, setDate] = useState(new Date());
const [draft, setDraft] = useState(new Date());
// Open: sync draft with current value
<Pressable onPress={() => { setDraft(date); setOpen(true); }}>
<Text>{date.toLocaleDateString()}</Text>
</Pressable>
<DatePickerModal
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setDate(draft); setOpen(false); }}
title="Select date"
>
<DatePicker value={draft} onChange={setDraft} locale="en-US" />
</DatePickerModal>DatePickerModalOverlay — style-based overlay
Same API as DatePickerModal but uses position: absolute instead of RN's Modal.
Avoids conflicts with toasts, sheets, and other overlays. Must be placed at root level.
import { DatePicker, DatePickerModalOverlay } from '@elberpg/ex-date-picker';
<DatePickerModalOverlay
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setDate(draft); setOpen(false); }}
title="Select date"
>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModalOverlay>TimePickerModal / TimePickerModalOverlay
Same API as the date variants. Just swap DatePicker for TimePicker as children.
import { TimePicker, TimePickerModal } from '@elberpg/ex-date-picker';
<TimePickerModal
visible={open}
onClose={() => setOpen(false)}
onConfirm={() => { setTime(draft); setOpen(false); }}
title="Select time"
clearable
onClear={() => { setTime(undefined); setOpen(false); }}
>
<TimePicker value={draft} onChange={setDraft} use12h />
</TimePickerModal>Modal props — PickerModalProps
Shared by DatePickerModal, DatePickerModalOverlay, TimePickerModal, TimePickerModalOverlay.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| visible | boolean | — | Shows or hides the modal |
| onClose | () => void | — | Called when the backdrop or Cancel is tapped |
| onConfirm | () => void | — | Called when Confirm is tapped |
| children | ReactNode | — | The picker to render inside |
| title | string | — | Modal title |
| confirmLabel | string | 'Confirm' | Confirm button text |
| cancelLabel | string | 'Cancel' | Cancel button text |
| clearable | boolean | false | Show a Clear button |
| clearLabel | string | 'Clear' | Clear button text |
| onClear | () => void | — | Called when Clear is tapped |
| btnConfirm | ({ onPress }) => ReactNode | — | Replace the Confirm button |
| btnCancel | ({ onPress }) => ReactNode | — | Replace the Cancel button |
| btnClear | ({ onPress }) => ReactNode | — | Replace the Clear button |
Global styles
There are two separate style objects — one for the picker, one for the modal shell.
ExDatePicker.styles — picker (scroll columns)
import { ExDatePicker } from '@elberpg/ex-date-picker';
ExDatePicker.styles.selectedColor = '#0a84ff';
ExDatePicker.styles.unselectedColor = '#8e8e93';
ExDatePicker.styles.itemHeight = 52;
ExDatePicker.styles.fontFamily = 'Georgia';
ExDatePicker.styles.lineColor = '#3a3a3c';
ExDatePicker.styles.columnLabel = { fontSize: 11, color: '#888', textTransform: 'uppercase' };| Field | Type | Description |
|-------|------|-------------|
| selectedColor | string | Color of the selected scroll item |
| unselectedColor | string | Color of unselected scroll items |
| itemHeight | number | Height of each scroll row in px |
| fontFamily | string | Font family for scroll item text |
| lineColor | string | Color of the two selection indicator lines |
| columnLabel | TextStyle | Column labels (DAY · MONTH · YEAR / HOUR · MIN) |
ExDatePickerModal.styles — modal shell
import { ExDatePickerModal } from '@elberpg/ex-date-picker';
ExDatePickerModal.styles.card = {
backgroundColor: '#1c1c1e',
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
};
ExDatePickerModal.styles.title = { fontSize: 17, fontWeight: '700', color: '#fff' };
ExDatePickerModal.styles.btnConfirm = { backgroundColor: '#0a84ff' };
ExDatePickerModal.styles.btnConfirmText = { fontSize: 15, fontWeight: '600', color: '#fff' };
ExDatePickerModal.styles.backdropColor = 'rgba(0,0,0,0.65)';| Field | Type | Description |
|-------|------|-------------|
| card | ViewStyle | Main card (background, border radius, padding) |
| pill | ViewStyle | Drag pill at the top |
| title | TextStyle | Modal title (fontSize, color, fontFamily…) |
| headerBorder | ViewStyle | Separator below the title |
| btnConfirm | ViewStyle | Confirm button container |
| btnCancel | ViewStyle | Cancel button container |
| btnClear | ViewStyle | Clear button container |
| btnConfirmText | TextStyle | Confirm button text |
| btnCancelText | TextStyle | Cancel button text |
| btnClearText | TextStyle | Clear button text |
| backdropColor | string | Backdrop color + opacity (rgba(…)) |
Custom button example
<DatePickerModal
btnConfirm={({ onPress }) => (
<Pressable onPress={onPress} style={{ backgroundColor: '#0a84ff', borderRadius: 12, flex: 1, height: 48, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ color: '#fff', fontWeight: '700' }}>Done</Text>
</Pressable>
)}
>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>Important rule
Place modals outside any ScrollView.
// ✅ Correct
return (
<>
<SafeAreaView>
<ScrollView>...</ScrollView>
</SafeAreaView>
<DatePickerModal visible={open} onClose={onClose} onConfirm={onConfirm}>
<DatePicker value={draft} onChange={setDraft} />
</DatePickerModal>
</>
);
// ❌ Wrong — the modal scrolls with the content
return (
<ScrollView>
<DatePickerModal visible={open} .../>
</ScrollView>
);