UNPKG

react-native-flexi-datepicker

Version:

A highly customizable and flexible date picker component for React Native

442 lines (441 loc) 19 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importStar(require("react")); var react_native_1 = require("react-native"); var moment_1 = __importDefault(require("moment")); var localeUtils_1 = require("../utils/localeUtils"); // Add this import var SCREEN_WIDTH = react_native_1.Dimensions.get("window").width; var SCREEN_HEIGHT = react_native_1.Dimensions.get("window").height; var ITEM_HEIGHT = 40; var VISIBLE_ITEMS = 5; var IOSDatePicker = function (_a) { var isVisible = _a.isVisible, onClose = _a.onClose, onDateChange = _a.onDateChange, _b = _a.initialDate, initialDate = _b === void 0 ? new Date() : _b, _c = _a.minDate, minDate = _c === void 0 ? "1900-01-01" : _c, _d = _a.maxDate, maxDate = _d === void 0 ? "3000-12-31" : _d, _e = _a.theme, theme = _e === void 0 ? {} : _e, _f = _a.cancelButton, cancelButton = _f === void 0 ? "Reset" : _f, _g = _a.okButton, okButton = _g === void 0 ? "Done" : _g, localeData = _a.localeData; var _h = (0, react_1.useState)(function () { var date = (0, moment_1.default)(initialDate); if (localeData) { date.locale(localeData.abbr); } return date; }), selectedDate = _h[0], setSelectedDate = _h[1]; var _j = (0, react_1.useState)(false), isMonthYearPickerVisible = _j[0], setMonthYearPickerVisible = _j[1]; var _k = (0, react_1.useState)(selectedDate.month()), selectedMonthIndex = _k[0], setSelectedMonthIndex = _k[1]; (0, react_1.useEffect)(function () { if (localeData) { var abbr = localeData.abbr; var localeLoader = localeUtils_1.localeRequires[abbr]; if (localeLoader) { try { localeLoader(); moment_1.default.locale(abbr); console.log("Loaded locale: ".concat(abbr)); } catch (error) { console.warn("Failed to load locale: ".concat(abbr), error); moment_1.default.locale("en"); } } else { console.warn("Locale not found: ".concat(abbr)); moment_1.default.locale("en"); } } else { moment_1.default.locale("en"); } }, [localeData]); var years = (0, react_1.useMemo)(function () { return Array.from({ length: (0, moment_1.default)(maxDate).year() - (0, moment_1.default)(minDate).year() + 1 }, function (_, i) { return (0, moment_1.default)(minDate).year() + i; }); }, [minDate, maxDate]); var months = (0, react_1.useMemo)(function () { if (localeData) { return localeData.calendar.monthNames; } return moment_1.default.months(); }, [localeData]); var _l = (0, react_1.useState)(years.findIndex(function (year) { return year === selectedDate.year(); })), selectedYearIndex = _l[0], setSelectedYearIndex = _l[1]; var monthScrollY = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current; var yearScrollY = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current; var colors = __assign({ primary: "#007AFF", background: "#FFFFFF", text: "#000000", border: "#E5E5EA" }, theme); var styles = react_native_1.StyleSheet.create({ modalOverlay: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "rgba(0, 0, 0, 0.4)", }, pickerContainer: { backgroundColor: colors.background, borderRadius: 12, overflow: "hidden", width: SCREEN_WIDTH * 0.9, maxHeight: SCREEN_HEIGHT * 0.7, }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, borderBottomWidth: 1, borderBottomColor: colors.border, }, monthYearButton: { flexDirection: "row", alignItems: "center", }, monthYearText: { fontSize: 18, fontWeight: "bold", color: colors.text, marginRight: 4, }, arrowContainer: { flexDirection: "row", }, arrowButton: { padding: 8, }, arrowText: { fontSize: 20, color: colors.primary, }, calendarContainer: { padding: 16, }, weekDaysContainer: { flexDirection: "row", justifyContent: "space-around", marginBottom: 8, }, weekDayText: { color: colors.text, opacity: 0.5, }, daysContainer: { flexDirection: "row", flexWrap: "wrap", }, dayButton: { width: (SCREEN_WIDTH * 0.9 - 32) / 7, height: 40, justifyContent: "center", alignItems: "center", }, dayText: { color: colors.text, }, selectedDayText: { color: colors.primary, fontWeight: "bold", }, disabledDayText: { color: colors.text, opacity: 0.3, }, bottomContainer: { flexDirection: "row", justifyContent: "space-between", padding: 16, borderTopWidth: 1, borderTopColor: colors.border, }, bottomButton: { fontSize: 16, color: colors.primary, }, monthYearPickerContainer: { flexDirection: "row", height: ITEM_HEIGHT * VISIBLE_ITEMS, alignItems: "center", }, pickerColumn: { flex: 1, height: ITEM_HEIGHT * VISIBLE_ITEMS, }, pickerItem: { height: ITEM_HEIGHT, justifyContent: "center", alignItems: "center", }, pickerItemText: { fontSize: 20, color: colors.text, }, pickerHighlight: { position: "absolute", top: ((VISIBLE_ITEMS - 1) / 2) * ITEM_HEIGHT, left: 0, right: 0, height: ITEM_HEIGHT, borderTopWidth: 1, borderBottomWidth: 1, borderColor: colors.border, }, }); var renderWeekDays = (0, react_1.useCallback)(function () { var weekDays = localeData ? localeData.calendar.dayNamesShort : moment_1.default.weekdaysShort(true); return (<react_native_1.View style={styles.weekDaysContainer}> {weekDays.map(function (day) { return (<react_native_1.Text key={day} style={styles.weekDayText}> {day} </react_native_1.Text>); })} </react_native_1.View>); }, [styles, localeData]); var renderDays = (0, react_1.useCallback)(function () { var days = []; var daysInMonth = selectedDate.daysInMonth(); var firstDayOfMonth = selectedDate.clone().startOf("month").day(); for (var i = 0; i < firstDayOfMonth; i++) { days.push(<react_native_1.View key={"empty-".concat(i)} style={styles.dayButton}/>); } var _loop_1 = function (i) { var date = selectedDate.clone().date(i); var isSelected = date.isSame(selectedDate, "day"); var isDisabled = date.isBefore((0, moment_1.default)(minDate)) || date.isAfter((0, moment_1.default)(maxDate)); days.push(<react_native_1.TouchableOpacity key={i} style={styles.dayButton} onPress={function () { return !isDisabled && setSelectedDate(date); }} disabled={isDisabled}> <react_native_1.Text style={[ styles.dayText, isSelected && styles.selectedDayText, isDisabled && styles.disabledDayText, ]}> {i} </react_native_1.Text> </react_native_1.TouchableOpacity>); }; for (var i = 1; i <= daysInMonth; i++) { _loop_1(i); } return <react_native_1.View style={styles.daysContainer}>{days}</react_native_1.View>; }, [selectedDate, minDate, maxDate, styles]); var changeMonth = (0, react_1.useCallback)(function (increment) { setSelectedDate(function (prev) { return prev.clone().add(increment, "month"); }); }, []); var renderPickerItems = (0, react_1.useCallback)(function (_a) { var items = _a.items, scrollY = _a.scrollY, onMomentumScrollEnd = _a.onMomentumScrollEnd, itemType = _a.itemType, initialScrollIndex = _a.initialScrollIndex; var AnimatedFlatList = react_native_1.Animated.createAnimatedComponent(react_native_1.FlatList); var handleScroll = function (event) { var index = Math.round(event.nativeEvent.contentOffset.y / ITEM_HEIGHT); console.log("Scrolling ".concat(itemType, ". Current index:"), index); }; var handleMomentumScrollEnd = function (event) { var index = Math.round(event.nativeEvent.contentOffset.y / ITEM_HEIGHT); onMomentumScrollEnd(index); }; return (<AnimatedFlatList data={items} keyExtractor={function (item) { return item.toString(); }} showsVerticalScrollIndicator={false} snapToInterval={ITEM_HEIGHT} decelerationRate="fast" bounces={false} onScroll={react_native_1.Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true, listener: handleScroll, })} onMomentumScrollEnd={handleMomentumScrollEnd} renderItem={function (_a) { var item = _a.item, index = _a.index; var position = react_native_1.Animated.subtract(index * ITEM_HEIGHT, scrollY); var opacity = position.interpolate({ inputRange: [ -ITEM_HEIGHT * 2, -ITEM_HEIGHT, 0, ITEM_HEIGHT, ITEM_HEIGHT * 2, ], outputRange: [0.3, 0.6, 1, 0.6, 0.3], extrapolate: "clamp", }); var scale = position.interpolate({ inputRange: [-ITEM_HEIGHT, 0, ITEM_HEIGHT], outputRange: [0.8, 1, 0.8], extrapolate: "clamp", }); return (<react_native_1.Animated.View style={[ styles.pickerItem, { opacity: opacity, transform: [{ scale: scale }], }, ]}> <react_native_1.Text style={styles.pickerItemText}> {item.toString()} </react_native_1.Text> </react_native_1.Animated.View>); }} getItemLayout={function (_, index) { return ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index: index, }); }} initialScrollIndex={initialScrollIndex} contentContainerStyle={{ paddingVertical: ((VISIBLE_ITEMS - 1) / 2) * ITEM_HEIGHT, }}/>); }, [styles]); (0, react_1.useEffect)(function () { if (isMonthYearPickerVisible) { monthScrollY.setValue(-selectedMonthIndex * ITEM_HEIGHT); yearScrollY.setValue(-selectedYearIndex * ITEM_HEIGHT); } }, [ isMonthYearPickerVisible, selectedMonthIndex, selectedYearIndex, monthScrollY, yearScrollY, ]); var renderMonthYearPicker = (0, react_1.useCallback)(function () { return (<react_native_1.View style={styles.monthYearPickerContainer}> <react_native_1.View style={styles.pickerHighlight}/> <react_native_1.View style={styles.pickerColumn}> {renderPickerItems({ items: months, scrollY: monthScrollY, onMomentumScrollEnd: function (index) { setSelectedMonthIndex(index); console.log("Month scroll ended. Selected index:", index); }, itemType: "month", initialScrollIndex: selectedMonthIndex, })} </react_native_1.View> <react_native_1.View style={styles.pickerColumn}> {renderPickerItems({ items: years, scrollY: yearScrollY, onMomentumScrollEnd: function (index) { setSelectedYearIndex(index); console.log("Year scroll ended. Selected index:", index); }, itemType: "year", initialScrollIndex: selectedYearIndex, })} </react_native_1.View> </react_native_1.View>); }, [ months, years, monthScrollY, yearScrollY, renderPickerItems, styles, selectedMonthIndex, selectedYearIndex, ]); var handleMonthYearSelection = (0, react_1.useCallback)(function () { var selectedYear = years[selectedYearIndex]; var newDate = (0, moment_1.default)() .locale(localeData ? localeData.abbr : "en") .year(selectedYear) .month(selectedMonthIndex) .date(1); if (newDate.isBefore((0, moment_1.default)(minDate))) { setSelectedDate((0, moment_1.default)(minDate).locale(localeData ? localeData.abbr : "en")); } else if (newDate.isAfter((0, moment_1.default)(maxDate))) { setSelectedDate((0, moment_1.default)(maxDate).locale(localeData ? localeData.abbr : "en")); } else { setSelectedDate(newDate); } console.log("Final selected date:", newDate.format("YYYY-MM-DD")); setMonthYearPickerVisible(false); }, [ selectedMonthIndex, selectedYearIndex, years, minDate, maxDate, localeData, ]); (0, react_1.useEffect)(function () { if (localeData) { setSelectedDate(function (prevDate) { return prevDate.clone().locale(localeData.abbr); }); } }, [localeData]); return (<react_native_1.Modal visible={isVisible} transparent animationType="fade" onRequestClose={onClose}> <react_native_1.View style={styles.modalOverlay}> <react_native_1.View style={styles.pickerContainer}> <react_native_1.View style={styles.header}> <react_native_1.TouchableOpacity style={styles.monthYearButton} onPress={function () { setMonthYearPickerVisible(!isMonthYearPickerVisible); if (!isMonthYearPickerVisible) { monthScrollY.setValue(-selectedDate.month() * ITEM_HEIGHT); yearScrollY.setValue(-years.indexOf(selectedDate.year()) * ITEM_HEIGHT); } }}> <react_native_1.Text style={styles.monthYearText}> {localeData ? "".concat(localeData.calendar.monthNames[selectedDate.month()], " ").concat(selectedDate.format("YYYY")) : selectedDate.format("MMMM YYYY")} </react_native_1.Text> <react_native_1.Text style={[styles.arrowText, { fontSize: 12 }]}> {isMonthYearPickerVisible ? "▲" : "▼"} </react_native_1.Text> </react_native_1.TouchableOpacity> {!isMonthYearPickerVisible && (<react_native_1.View style={styles.arrowContainer}> <react_native_1.TouchableOpacity style={styles.arrowButton} onPress={function () { return changeMonth(-1); }}> <react_native_1.Text style={styles.arrowText}>{"<"}</react_native_1.Text> </react_native_1.TouchableOpacity> <react_native_1.TouchableOpacity style={styles.arrowButton} onPress={function () { return changeMonth(1); }}> <react_native_1.Text style={styles.arrowText}>{">"}</react_native_1.Text> </react_native_1.TouchableOpacity> </react_native_1.View>)} </react_native_1.View> {isMonthYearPickerVisible ? (renderMonthYearPicker()) : (<react_native_1.View style={styles.calendarContainer}> {renderWeekDays()} {renderDays()} </react_native_1.View>)} <react_native_1.View style={styles.bottomContainer}> <react_native_1.TouchableOpacity onPress={function () { if (isMonthYearPickerVisible) { setMonthYearPickerVisible(false); } else { onClose(); } }}> <react_native_1.Text style={styles.bottomButton}>{cancelButton}</react_native_1.Text> </react_native_1.TouchableOpacity> <react_native_1.TouchableOpacity onPress={function () { if (isMonthYearPickerVisible) { handleMonthYearSelection(); } else { onDateChange(selectedDate.format("YYYY-MM-DD")); onClose(); } }}> <react_native_1.Text style={styles.bottomButton}>{okButton}</react_native_1.Text> </react_native_1.TouchableOpacity> </react_native_1.View> </react_native_1.View> </react_native_1.View> </react_native_1.Modal>); }; exports.default = react_1.default.memo(IOSDatePicker);