react-native-calendar-strip
Version:
Easy to use and visually stunning calendar component for React Native
262 lines (234 loc) • 8.69 kB
JavaScript
/**
* Created by bogdanbegovic on 8/20/16.
*/
import React, { Component } from "react";
import PropTypes from "prop-types";
import {polyfill} from 'react-lifecycles-compat';
import { Text, View, LayoutAnimation, TouchableOpacity } from "react-native";
import styles from "./Calendar.style.js";
class CalendarDay extends Component {
static propTypes = {
date: PropTypes.object.isRequired,
onDateSelected: PropTypes.func.isRequired,
selected: PropTypes.bool.isRequired,
enabled: PropTypes.bool.isRequired,
marking: PropTypes.any,
markedDates: PropTypes.array,
showDayName: PropTypes.bool,
showDayNumber: PropTypes.bool,
calendarColor: PropTypes.string,
dateNameStyle: PropTypes.any,
dateNumberStyle: PropTypes.any,
weekendDateNameStyle: PropTypes.any,
weekendDateNumberStyle: PropTypes.any,
highlightDateNameStyle: PropTypes.any,
highlightDateNumberStyle: PropTypes.any,
disabledDateNameStyle: PropTypes.any,
disabledDateNumberStyle: PropTypes.any,
disabledDateOpacity: PropTypes.number,
styleWeekend: PropTypes.bool,
customStyle: PropTypes.object,
daySelectionAnimation: PropTypes.object,
allowDayTextScaling: PropTypes.bool
};
// Reference: https://medium.com/@Jpoliachik/react-native-s-layoutanimation-is-awesome-4a4d317afd3e
static defaultProps = {
daySelectionAnimation: {
type: "", // animations disabled by default
duration: 300,
borderWidth: 1,
borderHighlightColor: "black",
highlightColor: "yellow",
animType: LayoutAnimation.Types.easeInEaseOut,
animUpdateType: LayoutAnimation.Types.easeInEaseOut,
animProperty: LayoutAnimation.Properties.opacity,
animSpringDamping: undefined // Only applicable for LayoutAnimation.Types.spring,
},
styleWeekend: true,
showDayName: true,
showDayNumber: true
};
constructor(props) {
super(props);
this.state = {
selected: props.selected,
...this.calcSizes(props)
};
}
componentDidUpdate(prevProps, prevState) {
newState = {};
let doStateUpdate = false;
if (this.props.selected !== prevProps.selected) {
if (this.props.daySelectionAnimation.type !== "") {
let configurableAnimation = {
duration: this.props.daySelectionAnimation.duration || 300,
create: {
type:
this.props.daySelectionAnimation.animType ||
LayoutAnimation.Types.easeInEaseOut,
property:
this.props.daySelectionAnimation.animProperty ||
LayoutAnimation.Properties.opacity
},
update: {
type:
this.props.daySelectionAnimation.animUpdateType ||
LayoutAnimation.Types.easeInEaseOut,
springDamping: this.props.daySelectionAnimation.animSpringDamping
},
delete: {
type:
this.props.daySelectionAnimation.animType ||
LayoutAnimation.Types.easeInEaseOut,
property:
this.props.daySelectionAnimation.animProperty ||
LayoutAnimation.Properties.opacity
}
};
LayoutAnimation.configureNext(configurableAnimation);
}
newState.selected = this.props.selected;
doStateUpdate = true;
}
if (prevProps.size !== this.props.size) {
newState = { ...newState, ...this.calcSizes(this.props) };
doStateUpdate = true;
}
if (doStateUpdate) {
this.setState(newState);
}
}
calcSizes(props) {
return {
containerSize: Math.round(props.size),
containerPadding: Math.round(props.size / 5),
containerBorderRadius: Math.round(props.size / 2),
dateNameFontSize: Math.round(props.size / 5),
dateNumberFontSize: Math.round(props.size / 2.9)
};
}
renderDots() {
if (!this.props.markedDates || this.props.markedDates.length === 0) {
return;
}
const marking = this.props.marking || {};
const baseDotStyle = [styles.dot, styles.visibleDot];
let validDots = <View style={[styles.dot]} />; // default empty view for no dots case
if (marking.dots && Array.isArray(marking.dots) && marking.dots.length > 0) {
// Filter out dots so that we we process only those items which have key and color property
validDots = marking.dots.filter(d => (d && d.color)).map((dot, index) => {
return (
<View key={dot.key ? dot.key : index} style={[baseDotStyle,
{ backgroundColor: marking.selected && dot.selectedDotColor ? dot.selectedDotColor : dot.color }]}/>
);
});
}
return (
<View style={styles.dotsContainer}>
{ validDots }
</View>
);
}
render() {
// Defaults for disabled state
let dateNameStyle = [styles.dateName, this.props.enabled ? this.props.dateNameStyle : this.props.disabledDateNameStyle];
let dateNumberStyle = [styles.dateNumber, this.props.enabled ? this.props.dateNumberStyle : this.props.disabledDateNumberStyle];
let dateViewStyle = this.props.enabled
? [{ backgroundColor: "transparent" }]
: [{ opacity: this.props.disabledDateOpacity }];
let customStyle = this.props.customStyle;
if (customStyle) {
dateNameStyle.push(customStyle.dateNameStyle);
dateNumberStyle.push(customStyle.dateNumberStyle);
dateViewStyle.push(customStyle.dateContainerStyle);
}
if (this.props.enabled && this.state.selected) {
// Enabled state
//The user can disable animation, so that is why I use selection type
//If it is background, the user have to input colors for animation
//If it is border, the user has to input color for border animation
switch (this.props.daySelectionAnimation.type) {
case "background":
dateViewStyle.push({ backgroundColor: this.props.daySelectionAnimation.highlightColor });
break;
case "border":
dateViewStyle.push({
borderColor: this.props.daySelectionAnimation.borderHighlightColor,
borderWidth: this.props.daySelectionAnimation.borderWidth
});
break;
default:
// No animation styling by default
break;
}
dateNameStyle = [styles.dateName, this.props.dateNameStyle];
dateNumberStyle = [styles.dateNumber, this.props.dateNumberStyle];
if (
this.props.styleWeekend &&
(this.props.date.isoWeekday() === 6 ||
this.props.date.isoWeekday() === 7)
) {
dateNameStyle = [
styles.weekendDateName,
this.props.weekendDateNameStyle
];
dateNumberStyle = [
styles.weekendDateNumber,
this.props.weekendDateNumberStyle
];
}
if (this.state.selected) {
dateNameStyle = [styles.dateName, this.props.highlightDateNameStyle];
dateNumberStyle = [
styles.dateNumber,
this.props.highlightDateNumberStyle
];
}
}
let responsiveDateContainerStyle = {
width: this.state.containerSize,
height: this.state.containerSize,
borderRadius: this.state.containerBorderRadius,
padding: this.state.containerPadding
};
return (
<TouchableOpacity
onPress={this.props.onDateSelected.bind(this, this.props.date)}
>
<View
key={this.props.date}
style={[
styles.dateContainer,
responsiveDateContainerStyle,
dateViewStyle
]}
>
{this.props.showDayName && (
<Text
style={[dateNameStyle, { fontSize: this.state.dateNameFontSize }]}
allowFontScaling={this.props.allowDayTextScaling}
>
{this.props.date.format("ddd").toUpperCase()}
</Text>
)}
{this.props.showDayNumber && (
<View>
<Text
style={[
{ fontSize: this.state.dateNumberFontSize },
dateNumberStyle
]}
allowFontScaling={this.props.allowDayTextScaling}
>
{this.props.date.date()}
</Text>
{ this.renderDots() }
</View>
)}
</View>
</TouchableOpacity>
);
}
}
polyfill(CalendarDay);
export default CalendarDay;