Files
luggage-list/src/components/DatePickerModal.js
Luna 30ee53fe75
All checks were successful
Luggage List Build / build-web (push) Successful in 28s
Luggage List Build / build-android (push) Successful in 5m59s
Luggage List Build / release (push) Successful in 11s
feat: add calendar date picker, nav icons, and input-focused keyboard scrolling
2026-04-18 13:12:51 +02:00

94 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useMemo, useState } from 'react';
import { Modal, Pressable, Text, View } from 'react-native';
import { styles } from '../styles';
import { todayYMD } from '../utils/date';
const WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
function toYMD(date) {
const y = date.getFullYear();
const m = `${date.getMonth() + 1}`.padStart(2, '0');
const d = `${date.getDate()}`.padStart(2, '0');
return `${y}-${m}-${d}`;
}
function parseFromYMD(value) {
if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return new Date();
const date = new Date(`${value}T00:00:00`);
return Number.isNaN(date.getTime()) ? new Date() : date;
}
function monthLabel(date) {
return date.toLocaleDateString(undefined, { month: 'long', year: 'numeric' });
}
function buildMonthGrid(viewDate) {
const first = new Date(viewDate.getFullYear(), viewDate.getMonth(), 1);
const startWeekday = (first.getDay() + 6) % 7;
const daysInMonth = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 0).getDate();
const cells = [];
for (let i = 0; i < startWeekday; i += 1) cells.push(null);
for (let day = 1; day <= daysInMonth; day += 1) {
cells.push(new Date(viewDate.getFullYear(), viewDate.getMonth(), day));
}
while (cells.length % 7 !== 0) cells.push(null);
return cells;
}
export default function DatePickerModal({ visible, value, onClose, onSelect, title = 'Pick date' }) {
const [viewDate, setViewDate] = useState(parseFromYMD(value || todayYMD()));
const grid = useMemo(() => buildMonthGrid(viewDate), [viewDate]);
const selected = value || todayYMD();
const goMonth = (diff) => {
setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + diff, 1));
};
return (
<Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
<View style={styles.dateModalBackdrop}>
<View style={styles.dateModalCard}>
<View style={styles.sectionRow}>
<Text style={styles.sectionTitle}>{title}</Text>
<Pressable onPress={onClose}>
<Text style={styles.closeText}>Close</Text>
</Pressable>
</View>
<View style={styles.calendarHeader}>
<Pressable style={styles.calendarNavBtn} onPress={() => goMonth(-1)}>
<Text style={styles.calendarNavText}></Text>
</Pressable>
<Text style={styles.calendarMonthText}>{monthLabel(viewDate)}</Text>
<Pressable style={styles.calendarNavBtn} onPress={() => goMonth(1)}>
<Text style={styles.calendarNavText}></Text>
</Pressable>
</View>
<View style={styles.calendarWeekRow}>
{WEEKDAYS.map((w) => (
<Text key={w} style={styles.calendarWeekday}>{w}</Text>
))}
</View>
<View style={styles.calendarGrid}>
{grid.map((cell, idx) => {
if (!cell) return <View key={`empty-${idx}`} style={styles.calendarCell} />;
const ymd = toYMD(cell);
const isSelected = ymd === selected;
return (
<Pressable key={ymd} style={[styles.calendarCell, isSelected && styles.calendarCellActive]} onPress={() => onSelect(ymd)}>
<Text style={[styles.calendarCellText, isSelected && styles.calendarCellTextActive]}>{cell.getDate()}</Text>
</Pressable>
);
})}
</View>
</View>
</View>
</Modal>
);
}