UNPKG

react-native-lunar-calendars

Version:

React Native Calendar Components with Lunar Calendar Support - Fork of react-native-calendars with Vietnamese lunar calendar functionality

406 lines (405 loc) 20.8 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); 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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __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 prop_types_1 = __importDefault(require("prop-types")); var xdate_1 = __importDefault(require("xdate")); var interface_1 = require("../interface"); var dateutils_1 = __importDefault(require("../dateutils")); var calendar_list_1 = __importDefault(require("../calendar-list")); var reservation_list_1 = __importDefault(require("./reservation-list")); var style_1 = __importDefault(require("./style")); var input_1 = require("../input"); var HEADER_HEIGHT = 104; var KNOB_HEIGHT = 24; //Fallback when RN version is < 0.44 var viewPropTypes = react_native_1.ViewPropTypes || react_native_1.View.propTypes; var AgendaView = /** @class */ (function (_super) { __extends(AgendaView, _super); function AgendaView(props) { var _this = _super.call(this, props) || this; _this.styles = (0, style_1.default)(props.theme); var windowSize = react_native_1.Dimensions.get('window'); _this.viewHeight = windowSize.height; _this.viewWidth = windowSize.width; _this.scrollTimeout = undefined; _this.headerState = 'idle'; _this.state = { scrollY: new react_native_1.Animated.Value(0), calendarIsReady: false, calendarScrollable: false, firstResevationLoad: false, selectedDay: (0, interface_1.parseDate)(_this.props.selected) || (0, xdate_1.default)(true), topDay: (0, interface_1.parseDate)(_this.props.selected) || (0, xdate_1.default)(true), }; _this.currentMonth = _this.state.selectedDay.clone(); _this.onLayout = _this.onLayout.bind(_this); _this.onScrollPadLayout = _this.onScrollPadLayout.bind(_this); _this.onTouchStart = _this.onTouchStart.bind(_this); _this.onTouchEnd = _this.onTouchEnd.bind(_this); _this.onStartDrag = _this.onStartDrag.bind(_this); _this.onSnapAfterDrag = _this.onSnapAfterDrag.bind(_this); _this.generateMarkings = _this.generateMarkings.bind(_this); _this.knobTracker = new input_1.VelocityTracker(); _this.state.scrollY.addListener(function (_a) { var value = _a.value; return _this.knobTracker.add(value); }); return _this; } AgendaView.prototype.calendarOffset = function () { return 90 - (this.viewHeight / 2); }; AgendaView.prototype.initialScrollPadPosition = function () { return Math.max(0, this.viewHeight - HEADER_HEIGHT); }; AgendaView.prototype.setScrollPadPosition = function (y, animated) { this.scrollPad._component.scrollTo({ x: 0, y: y, animated: animated }); }; AgendaView.prototype.onScrollPadLayout = function () { var _this = this; // When user touches knob, the actual component that receives touch events is a ScrollView. // It needs to be scrolled to the bottom, so that when user moves finger downwards, // scroll position actually changes (it would stay at 0, when scrolled to the top). this.setScrollPadPosition(this.initialScrollPadPosition(), false); // delay rendering calendar in full height because otherwise it still flickers sometimes setTimeout(function () { return _this.setState({ calendarIsReady: true }); }, 0); }; AgendaView.prototype.onLayout = function (event) { this.viewHeight = event.nativeEvent.layout.height; this.viewWidth = event.nativeEvent.layout.width; this.forceUpdate(); }; AgendaView.prototype.onTouchStart = function () { this.headerState = 'touched'; if (this.knob) { this.knob.setNativeProps({ style: { opacity: 0.5 } }); } }; AgendaView.prototype.onTouchEnd = function () { if (this.knob) { this.knob.setNativeProps({ style: { opacity: 1 } }); } if (this.headerState === 'touched') { this.setScrollPadPosition(0, true); this.enableCalendarScrolling(); } this.headerState = 'idle'; }; AgendaView.prototype.onStartDrag = function () { this.headerState = 'dragged'; this.knobTracker.reset(); }; AgendaView.prototype.onSnapAfterDrag = function (e) { // on Android onTouchEnd is not called if dragging was started this.onTouchEnd(); var currentY = e.nativeEvent.contentOffset.y; this.knobTracker.add(currentY); var projectedY = currentY + this.knobTracker.estimateSpeed() * 250 /*ms*/; var maxY = this.initialScrollPadPosition(); var snapY = (projectedY > maxY / 2) ? maxY : 0; this.setScrollPadPosition(snapY, true); if (snapY === 0) { this.enableCalendarScrolling(); } }; AgendaView.prototype.onVisibleMonthsChange = function (months) { var _this = this; if (this.props.items && !this.state.firstResevationLoad) { clearTimeout(this.scrollTimeout); this.scrollTimeout = setTimeout(function () { if (_this.props.loadItemsForMonth && _this._isMounted) { _this.props.loadItemsForMonth(months[0]); } }, 200); } }; AgendaView.prototype.loadReservations = function (props) { var _this = this; if ((!props.items || !Object.keys(props.items).length) && !this.state.firstResevationLoad) { this.setState({ firstResevationLoad: true }, function () { if (_this.props.loadItemsForMonth) { _this.props.loadItemsForMonth((0, interface_1.xdateToData)(_this.state.selectedDay)); } }); } }; AgendaView.prototype.componentWillMount = function () { this._isMounted = true; }; AgendaView.prototype.componentDidMount = function () { this.loadReservations(this.props); }; AgendaView.prototype.componentWillUnmount = function () { this._isMounted = false; }; AgendaView.prototype.componentWillReceiveProps = function (props) { if (props.items) { this.setState({ firstResevationLoad: false }); } else { this.loadReservations(props); } }; AgendaView.prototype.enableCalendarScrolling = function () { this.setState({ calendarScrollable: true }); if (this.props.onCalendarToggled) { this.props.onCalendarToggled(true); } // Enlarge calendarOffset here as a workaround on iOS to force repaint. // Otherwise the month after current one or before current one remains invisible. // The problem is caused by overflow: 'hidden' style, which we need for dragging // to be performant. // Another working solution for this bug would be to set removeClippedSubviews={false} // in CalendarList listView, but that might impact performance when scrolling // month list in expanded CalendarList. // Further info https://github.com/facebook/react-native/issues/1831 this.calendar.scrollToDay(this.state.selectedDay, this.calendarOffset() + 1, true); }; AgendaView.prototype._chooseDayFromCalendar = function (d) { this.chooseDay(d, !this.state.calendarScrollable); }; AgendaView.prototype.chooseDay = function (d, optimisticScroll) { var day = (0, interface_1.parseDate)(d); this.setState({ calendarScrollable: false, selectedDay: day.clone() }); if (this.props.onCalendarToggled) { this.props.onCalendarToggled(false); } if (!optimisticScroll) { this.setState({ topDay: day.clone() }); } this.setScrollPadPosition(this.initialScrollPadPosition(), true); this.calendar.scrollToDay(day, this.calendarOffset(), true); if (this.props.loadItemsForMonth) { this.props.loadItemsForMonth((0, interface_1.xdateToData)(day)); } if (this.props.onDayPress) { this.props.onDayPress((0, interface_1.xdateToData)(day)); } }; AgendaView.prototype.renderReservations = function () { var _this = this; return (<reservation_list_1.default rowHasChanged={this.props.rowHasChanged} renderItem={this.props.renderItem} renderDay={this.props.renderDay} renderEmptyDate={this.props.renderEmptyDate} reservations={this.props.items} selectedDay={this.state.selectedDay} renderEmptyData={this.props.renderEmptyData} topDay={this.state.topDay} onDayChange={this.onDayChange.bind(this)} onScroll={function () { }} ref={function (c) { return _this.list = c; }} theme={this.props.theme}/>); }; AgendaView.prototype.onDayChange = function (day) { var newDate = (0, interface_1.parseDate)(day); var withAnimation = dateutils_1.default.sameMonth(newDate, this.state.selectedDay); this.calendar.scrollToDay(day, this.calendarOffset(), withAnimation); this.setState({ selectedDay: (0, interface_1.parseDate)(day) }); if (this.props.onDayChange) { this.props.onDayChange((0, interface_1.xdateToData)(newDate)); } }; AgendaView.prototype.generateMarkings = function () { var _a; var _this = this; var markings = this.props.markedDates; if (!markings) { markings = {}; Object.keys(this.props.items || {}).forEach(function (key) { if (_this.props.items[key] && _this.props.items[key].length) { markings[key] = { marked: true }; } }); } var key = this.state.selectedDay.toString('yyyy-MM-dd'); return __assign(__assign({}, markings), (_a = {}, _a[key] = __assign(__assign({}, (markings[key] || {})), { selected: true }), _a)); }; AgendaView.prototype.render = function () { var _this = this; var agendaHeight = Math.max(0, this.viewHeight - HEADER_HEIGHT); var weekDaysNames = dateutils_1.default.weekDayNames(this.props.firstDay); var weekdaysStyle = [this.styles.weekdays, { opacity: this.state.scrollY.interpolate({ inputRange: [agendaHeight - HEADER_HEIGHT, agendaHeight], outputRange: [0, 1], extrapolate: 'clamp', }), transform: [{ translateY: this.state.scrollY.interpolate({ inputRange: [Math.max(0, agendaHeight - HEADER_HEIGHT), agendaHeight], outputRange: [-HEADER_HEIGHT, 0], extrapolate: 'clamp', }) }] }]; var headerTranslate = this.state.scrollY.interpolate({ inputRange: [0, agendaHeight], outputRange: [agendaHeight, 0], extrapolate: 'clamp', }); var contentTranslate = this.state.scrollY.interpolate({ inputRange: [0, agendaHeight], outputRange: [0, agendaHeight / 2], extrapolate: 'clamp', }); var headerStyle = [ this.styles.header, { bottom: agendaHeight, transform: [{ translateY: headerTranslate }] }, ]; if (!this.state.calendarIsReady) { // limit header height until everything is setup for calendar dragging headerStyle.push({ height: 0 }); // fill header with appStyle.calendarBackground background to reduce flickering weekdaysStyle.push({ height: HEADER_HEIGHT }); } var shouldAllowDragging = !this.props.hideKnob && !this.state.calendarScrollable; var scrollPadPosition = (shouldAllowDragging ? HEADER_HEIGHT : 0) - KNOB_HEIGHT; var scrollPadStyle = { position: 'absolute', width: 80, height: KNOB_HEIGHT, top: scrollPadPosition, left: (this.viewWidth - 80) / 2, }; var knob = (<react_native_1.View style={this.styles.knobContainer}/>); if (!this.props.hideKnob) { var knobView = this.props.renderKnob ? this.props.renderKnob() : (<react_native_1.View style={this.styles.knob}/>); knob = this.state.calendarScrollable ? null : (<react_native_1.View style={this.styles.knobContainer}> <react_native_1.View ref={function (c) { return _this.knob = c; }}>{knobView}</react_native_1.View> </react_native_1.View>); } return (<react_native_1.View onLayout={this.onLayout} style={[this.props.style, { flex: 1, overflow: 'hidden' }]}> <react_native_1.View style={this.styles.reservations}> {this.renderReservations()} </react_native_1.View> <react_native_1.Animated.View style={headerStyle}> <react_native_1.Animated.View style={{ flex: 1, transform: [{ translateY: contentTranslate }] }}> <calendar_list_1.default onLayout={function () { _this.calendar.scrollToDay(_this.state.selectedDay.clone(), _this.calendarOffset(), false); }} theme={this.props.theme} onVisibleMonthsChange={this.onVisibleMonthsChange.bind(this)} ref={function (c) { return _this.calendar = c; }} minDate={this.props.minDate} maxDate={this.props.maxDate} current={this.currentMonth} markedDates={this.generateMarkings()} markingType={this.props.markingType} removeClippedSubviews={this.props.removeClippedSubviews} onDayPress={this._chooseDayFromCalendar.bind(this)} scrollingEnabled={this.state.calendarScrollable} hideExtraDays={this.state.calendarScrollable} firstDay={this.props.firstDay} monthFormat={this.props.monthFormat} pastScrollRange={this.props.pastScrollRange} futureScrollRange={this.props.futureScrollRange} dayComponent={this.props.dayComponent} disabledByDefault={this.props.disabledByDefault}/> </react_native_1.Animated.View> {knob} </react_native_1.Animated.View> <react_native_1.Animated.View style={weekdaysStyle}> {weekDaysNames.map(function (day) { return (<react_native_1.Text allowFontScaling={false} key={day} style={_this.styles.weekday} numberOfLines={1}>{day}</react_native_1.Text>); })} </react_native_1.Animated.View> <react_native_1.Animated.ScrollView ref={function (c) { return _this.scrollPad = c; }} overScrollMode='never' showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} style={scrollPadStyle} scrollEventThrottle={1} onTouchStart={this.onTouchStart} onTouchEnd={this.onTouchEnd} onScrollBeginDrag={this.onStartDrag} onScrollEndDrag={this.onSnapAfterDrag} onScroll={react_native_1.Animated.event([{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }], { useNativeDriver: true })}> <react_native_1.View style={{ height: agendaHeight + KNOB_HEIGHT }} onLayout={this.onScrollPadLayout}/> </react_native_1.Animated.ScrollView> </react_native_1.View>); }; AgendaView.propTypes = { // Specify theme properties to override specific styles for calendar parts. Default = {} theme: prop_types_1.default.object, // agenda container style style: viewPropTypes.style, // the list of items that have to be displayed in agenda. If you want to render item as empty date // the value of date key has to be an empty array []. If there exists no value for date key it is // considered that the date in question is not yet loaded items: prop_types_1.default.object, // callback that gets called when items for a certain month should be loaded (month became visible) loadItemsForMonth: prop_types_1.default.func, // callback that fires when the calendar is opened or closed onCalendarToggled: prop_types_1.default.func, // callback that gets called on day press onDayPress: prop_types_1.default.func, // callback that gets called when day changes while scrolling agenda list onDaychange: prop_types_1.default.func, // specify how each item should be rendered in agenda renderItem: prop_types_1.default.func, // specify how each date should be rendered. day can be undefined if the item is not first in that day. renderDay: prop_types_1.default.func, // specify how agenda knob should look like renderKnob: prop_types_1.default.func, // specify how empty date content with no items should be rendered renderEmptyDay: prop_types_1.default.func, // specify what should be rendered instead of ActivityIndicator renderEmptyData: prop_types_1.default.func, // specify your item comparison function for increased performance rowHasChanged: prop_types_1.default.func, // Max amount of months allowed to scroll to the past. Default = 50 pastScrollRange: prop_types_1.default.number, // Max amount of months allowed to scroll to the future. Default = 50 futureScrollRange: prop_types_1.default.number, // initially selected day selected: prop_types_1.default.any, // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined minDate: prop_types_1.default.any, // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined maxDate: prop_types_1.default.any, // Collection of dates that have to be marked. Default = items markedDates: prop_types_1.default.object, // Optional marking type if custom markedDates are provided markingType: prop_types_1.default.string, // Hide knob button. Default = false hideKnob: prop_types_1.default.bool, // Month format in calendar title. Formatting values: http://arshaw.com/xdate/#Formatting monthFormat: prop_types_1.default.string }; return AgendaView; }(react_1.Component)); exports.default = AgendaView;