import React, { useState } from "react"; import { View, Text, Modal, TouchableOpacity, StyleSheet, } from "react-native"; // ─── Helper ─────────────────────────────────────────────────────────────────── const BULAN = [ "Januari","Februari","Maret","April","Mei","Juni", "Juli","Agustus","September","Oktober","November","Desember", ]; const HARI = ["Min","Sen","Sel","Rab","Kam","Jum","Sab"]; export const toISO = (d: Date) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`; export const fromISO = (s: string): Date => { const [y,m,d] = s.split("-").map(Number); return new Date(y, m-1, d); }; export const toDisplay = (s: string): string => { const d = fromISO(s); return `${String(d.getDate()).padStart(2,"0")}/${String(d.getMonth()+1).padStart(2,"0")}/${d.getFullYear()}`; }; function getDaysInMonth(year: number, month: number) { return new Date(year, month + 1, 0).getDate(); } // ─── Props ──────────────────────────────────────────────────────────────────── interface CalendarPickerProps { visible: boolean; value: string; // "yyyy-mm-dd" minDate?: string; maxDate?: string; title?: string; onConfirm: (date: string) => void; onClose: () => void; } // ─── Komponen ───────────────────────────────────────────────────────────────── export default function CalendarPicker({ visible, value, minDate, maxDate, title = "Pilih Tanggal", onConfirm, onClose, }: CalendarPickerProps) { const initDate = fromISO(value); const [viewYear, setViewYear] = useState(initDate.getFullYear()); const [viewMonth, setViewMonth] = useState(initDate.getMonth()); const [selected, setSelected] = useState(value); const todayISO = toISO(new Date()); const totalDays = getDaysInMonth(viewYear, viewMonth); const firstDay = new Date(viewYear, viewMonth, 1).getDay(); // 0=Minggu // Navigasi bulan const prevMonth = () => { if (viewMonth === 0) { setViewMonth(11); setViewYear(y => y - 1); } else setViewMonth(m => m - 1); }; const nextMonth = () => { if (viewMonth === 11) { setViewMonth(0); setViewYear(y => y + 1); } else setViewMonth(m => m + 1); }; const isDisabled = (iso: string) => { if (minDate && iso < minDate) return true; if (maxDate && iso > maxDate) return true; return false; }; const isToday = (iso: string) => iso === todayISO; const isSelected = (iso: string) => iso === selected; // Buat grid hari (null = sel kosong sebelum hari pertama) const cells: (number | null)[] = [ ...Array(firstDay).fill(null), ...Array.from({ length: totalDays }, (_, i) => i + 1), ]; // Lengkapi sampai kelipatan 7 while (cells.length % 7 !== 0) cells.push(null); return ( {}}> {/* Judul */} {title} {/* Nav bulan */} {BULAN[viewMonth]} {viewYear} {/* Header hari */} {HARI.map(h => ( {h} ))} {/* Grid tanggal */} {cells.map((day, i) => { if (!day) return ; const iso = `${viewYear}-${String(viewMonth+1).padStart(2,"0")}-${String(day).padStart(2,"0")}`; const disabled = isDisabled(iso); const today = isToday(iso); const sel = isSelected(iso); const isSun = (firstDay + day - 1) % 7 === 0; return ( setSelected(iso)} style={[ s.dayCell, sel && s.daySel, today && !sel && s.dayToday, ]} > {day} {today && !sel && } ); })} {/* Preview tanggal dipilih */} Dipilih:{" "} {toDisplay(selected)} {/* Tombol aksi */} Batal { onConfirm(selected); onClose(); }} style={s.btnOk}> Pilih ); } // ─── Styles ─────────────────────────────────────────────────────────────────── const CELL_SIZE = 38; const s = StyleSheet.create({ overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.45)", justifyContent: "center", alignItems: "center", }, card: { backgroundColor: "#fff", borderRadius: 20, padding: 20, width: 320, shadowColor: "#000", shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.18, shadowRadius: 20, elevation: 16, }, title: { fontSize: 16, fontWeight: "800", color: "#0f172a", textAlign: "center", marginBottom: 16, }, // Nav navRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }, navBtn: { width: 36, height: 36, borderRadius: 18, backgroundColor: "#f1f5f9", alignItems: "center", justifyContent: "center" }, navArrow: { fontSize: 22, color: "#4f46e5", lineHeight: 26 }, navLabel: { fontSize: 15, fontWeight: "700", color: "#0f172a" }, // Header hari weekRow: { flexDirection: "row", marginBottom: 6 }, weekCell: { width: CELL_SIZE, textAlign: "center", fontSize: 11, fontWeight: "600", color: "#94a3b8" }, // Grid grid: { flexDirection: "row", flexWrap: "wrap" }, dayCell: { width: CELL_SIZE, height: CELL_SIZE, alignItems: "center", justifyContent: "center", borderRadius: CELL_SIZE / 2, marginBottom: 2, }, daySel: { backgroundColor: "#4f46e5" }, dayToday: { borderWidth: 1.5, borderColor: "#4f46e5" }, dayText: { fontSize: 13, color: "#334155" }, todayDot: { position: "absolute", bottom: 4, width: 4, height: 4, borderRadius: 2, backgroundColor: "#4f46e5", }, // Preview previewRow: { backgroundColor: "#f8fafc", borderRadius: 10, padding: 10, marginTop: 12, alignItems: "center", }, previewText: { fontSize: 12, color: "#64748b" }, previewVal: { fontWeight: "700", color: "#0f172a" }, // Buttons btnRow: { flexDirection: "row", gap: 10, marginTop: 14 }, btnCancel: { flex: 1, paddingVertical: 12, borderRadius: 12, backgroundColor: "#f1f5f9", alignItems: "center" }, btnCancelText:{ fontSize: 14, color: "#64748b", fontWeight: "600" }, btnOk: { flex: 2, paddingVertical: 12, borderRadius: 12, backgroundColor: "#4f46e5", alignItems: "center" }, btnOkText: { fontSize: 14, color: "#fff", fontWeight: "800" }, });