Skip to content

Commit

Permalink
horizontal calendar list and few twicks
Browse files Browse the repository at this point in the history
  • Loading branch information
rghorbani committed Apr 19, 2018
1 parent 7994644 commit d91e5a4
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 11 deletions.
62 changes: 60 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Event handler callbacks are called with `calendar objects` like this:

Parameters that require date types accept YYYY-MM-DD formated datestrings (in gregorian), JavaScript date objects, `calendar objects` and UTC timestamps.

Calendars can be localized by adding custom locales using `Moment's i18n` object:
Calendars can be localized by adding custom locales using `Moment's i18n` functions:

```javascript
import Moment from 'moment';
Expand Down Expand Up @@ -165,11 +165,47 @@ Period marking
'2012-05-23': {selected: true, endingDay: true, color: 'green', textColor: 'gray'},
'2012-05-04': {disabled: true, startingDay: true, color: 'green', endingDay: true}
}}
// Date marking style [simple/period/multi-dot]. Default = 'simple'
// Date marking style [simple/period/multi-dot/custom]. Default = 'simple'
markingType={'period'}
/>
```

Custom marking allows you to customize each marker with custom styles.

<kbd>
<img height=50 src="https://github.com/rghorbani/react-native-general-calendars/blob/master/demo/custom.png?raw=true">
</kbd>

```javascript
<Calendar
// Date marking style [simple/period/multi-dot/single]. Default = 'simple'
markingType={'custom'}
markedDates={{
'2018-03-28': {
customStyles: {
container: {
backgroundColor: 'green',
},
text: {
color: 'black',
fontWeight: 'bold'
},
},
},
'2018-03-29': {
customStyles: {
container: {
backgroundColor: 'white',
elevation: 2
},
text: {
color: 'blue',
},
}
}}}
/>
```

Keep in mind that different marking types are not compatible. You can use just one marking style for calendar.

#### Displaying data loading indicator
Expand Down Expand Up @@ -207,6 +243,7 @@ The loading indicator next to month name will be displayed if `<Calendar />` has
textDayFontFamily: 'monospace',
textMonthFontFamily: 'monospace',
textDayHeaderFontFamily: 'monospace',
textMonthFontWeight: 'bold',
textDayFontSize: 16,
textMonthFontSize: 16,
textDayHeaderFontSize: 16
Expand Down Expand Up @@ -287,6 +324,27 @@ If you implement an awesome day component please make a PR so that other people
/>
```

#### Horizontal CalendarList

<kbd>
<img src="https://github.com/rghorbani/react-native-general-calendars/blob/master/demo/horizontal-calendar-list.gif?raw=true">
</kbd>

You can also make the `CalendarList` scroll horizontally. To do that you need to pass specific props to the `CalendarList`:

```javascript
<CalendarList
// Enable horizontal scrolling, default = false
horizontal={true}
// Enable paging on horizontal, default = false
pagingEnabled={true}
// Set custom calendarWidth.
calendarWidth={320}
...calendarListParams
...calendarParams
/>
```

### Agenda

Coming soon
Expand Down
Binary file added demo/custom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/horizontal-calendar-list.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/marking5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-general-calendars",
"version": "1.2.3",
"version": "1.3.0",
"description": "React Native Calendar Components with support of Gregorian & Jalaali Calendars",
"main": "src/index.js",
"scripts": {
Expand Down
25 changes: 19 additions & 6 deletions src/calendar-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,26 @@ const React = require('react');
const PropTypes = require('prop-types');
const Moment = require('moment');
const jMoment = require('moment-jalaali');
const { FlatList, Platform, StyleSheet } = require('react-native');
const { Dimensions, FlatList, Platform, StyleSheet } = require('react-native');

const CalendarListItem = require('./item');
const Calendar = require('../calendar');
const defaultStyle = require('../style');
const dateutils = require('../dateutils');
const { xdateToData, parseDate } = require('../interface');

const { width } = Dimensions.get('window');

class CalendarList extends React.Component {
static propTypes = {
...Calendar.propTypes,

// Whether the scroll is horizontal
horizontal: PropTypes.bool,

// Used when calendar scroll is horizontal, default is device width, pagination should be disabled
calendarWidth: PropTypes.number,

// hight of each calendar. Default = 360
calendarHeight: PropTypes.number,

Expand All @@ -43,12 +51,14 @@ class CalendarList extends React.Component {

static defaultProps = {
type: 'gregorian',
calendarWidth: width,
calendarHeight: 360,
pastScrollRange: 50,
futureScrollRange: 50,
showScrollIndicator: false,
scrollEnabled: true,
scrollsToTop: false,
removeClippedSubviews: Platform.OS === 'android' ? false : true,
};

constructor(props) {
Expand Down Expand Up @@ -91,9 +101,9 @@ class CalendarList extends React.Component {
initialized: false,
};

this.getItemLayout = this.getItemLayout.bind(this);
this.onViewableItemsChangedBound = this.onViewableItemsChanged.bind(this);
this.renderCalendar = this.renderCalendar.bind(this);
this.getItemLayout = this.getItemLayout.bind(this);
}

scrollToDay(d, offset, animated) {
Expand Down Expand Up @@ -193,11 +203,11 @@ class CalendarList extends React.Component {
}

renderCalendar({item}) {
return (<CalendarListItem item={item} calendarHeight={this.props.calendarHeight} {...this.props} />);
return (<CalendarListItem item={item} calendarHeight={this.props.calendarHeight} calendarWidth={this.props.horizontal ? this.props.calendarWidth : undefined} {...this.props} />);
}

getItemLayout(data, index) {
return {length: this.props.calendarHeight, offset: this.props.calendarHeight * index, index};
return {length: this.props.horizontal ? this.props.calendarWidth : this.props.calendarHeight, offset: (this.props.horizontal ? this.props.calendarWidth : this.props.calendarHeight) * index, index};
}

getMonthIndex(month) {
Expand All @@ -215,13 +225,16 @@ class CalendarList extends React.Component {
<FlatList
ref={(c) => this.listView = c}
style={[this.style.container, this.props.style]}
initialListSize={this.props.pastScrollRange * this.props.futureScrollRange + 1}
initialListSize={this.props.pastScrollRange + this.props.futureScrollRange + 1}
data={this.state.rows}
removeClippedSubviews={Platform.OS === 'android' ? false : true}
removeClippedSubviews={this.props.removeClippedSubviews}
pageSize={1}
horizontal={this.props.horizontal || false}
pagingEnabled={this.props.pagingEnabled}
onViewableItemsChanged={this.onViewableItemsChangedBound}
renderItem={this.renderCalendar}
showsVerticalScrollIndicator={this.props.showScrollIndicator}
showsHorizontalScrollIndicator={this.props.showScrollIndicator}
scrollEnabled={this.props.scrollEnabled}
keyExtractor={(item, index) => String(index)}
initialScrollIndex={this.state.openDate ? this.getMonthIndex(this.state.openDate) : false}
Expand Down
1 change: 1 addition & 0 deletions src/calendar/day/basic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Day extends React.Component {
markingChanged = (!(
this.props.marking.marked === nextProps.marking.marked
&& this.props.marking.selected === nextProps.marking.selected
&& this.props.marking.selectedColor === nextProps.marking.selectedColor
&& this.props.marking.dotColor === nextProps.marking.dotColor
&& this.props.marking.disabled === nextProps.marking.disabled));
} else {
Expand Down
153 changes: 153 additions & 0 deletions src/calendar/day/custom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Copyright 2016 Reza (github.com/rghorbani).
*
* @flow
*/

'use strict';

const React = require('react');
const PropTypes = require('prop-types');
const { Platform, StyleSheet, Text, TouchableOpacity } = require('react-native');

const defaultStyle = require('../../../style');

class Day extends React.Component {
static propTypes = {
// TODO: disabled props should be removed
state: PropTypes.oneOf(['selected', 'disabled', 'today', '']),

// Specify theme properties to override specific styles for calendar parts. Default = {}
theme: PropTypes.object,
marking: PropTypes.any,
onPress: PropTypes.func,
date: PropTypes.object
};

constructor(props) {
super(props);
this.style = styleConstructor(props.theme);
this.onDayPress = this.onDayPress.bind(this);
this.onDayLongPress = this.onDayLongPress.bind(this);
}

onDayPress() {
this.props.onPress(this.props.date);
}
onDayLongPress() {
this.props.onLongPress(this.props.date);
}

shouldComponentUpdate(nextProps) {
const changed = ['state', 'children', 'marking', 'onPress'].reduce((prev, next) => {
if (prev) {
return prev;
} else if (nextProps[next] !== this.props[next]) {
return next;
}
return prev;
}, false);
if (changed === 'marking') {
let markingChanged = false;
if (this.props.marking && nextProps.marking) {
markingChanged = (!(
this.props.marking.marked === nextProps.marking.marked
&& this.props.marking.selected === nextProps.marking.selected
&& this.props.marking.disabled === nextProps.marking.disabled));
} else {
markingChanged = true;
}
// console.log('marking changed', markingChanged);
return markingChanged;
} else {
// console.log('changed', changed);
return !!changed;
}
}

render() {
let containerStyle = [this.style.base];
let textStyle = [this.style.text];

let marking = this.props.marking || {};
if (marking && marking.constructor === Array && marking.length) {
marking = {
marking: true
};
}
const isDisabled = typeof marking.disabled !== 'undefined' ? marking.disabled : this.props.state === 'disabled';

if (marking.selected) {
containerStyle.push(this.style.selected);
} else if (isDisabled) {
textStyle.push(this.style.disabledText);
} else if (this.props.state === 'today') {
textStyle.push(this.style.todayText);
}

if (marking.customStyles && typeof marking.customStyles === 'object') {
const styles = marking.customStyles;
if (styles.container) {
if (styles.container.borderRadius === undefined) {
styles.container.borderRadius = 16;
}
containerStyle.push(styles.container);
}
if (styles.text) {
textStyle.push(styles.text);
}
}

return (
<TouchableOpacity
style={containerStyle}
onPress={this.onDayPress}
onLongPress={this.onDayLongPress}
activeOpacity={marking.activeOpacity}
disabled={marking.disableTouchEvent}
>
<Text allowFontScaling={false} style={textStyle}>{String(this.props.children)}</Text>
</TouchableOpacity>
);
}
}

const STYLESHEET_ID = 'stylesheet.day.single';

function styleConstructor(theme = {}) {
const appStyle = {...defaultStyle, ...theme};
return StyleSheet.create({
base: {
width: 32,
height: 32,
alignItems: 'center'
},
text: {
marginTop: Platform.OS === 'android' ? 4 : 6,
fontSize: appStyle.textDayFontSize,
fontFamily: appStyle.textDayFontFamily,
fontWeight: '300',
color: appStyle.dayTextColor,
backgroundColor: 'rgba(255, 255, 255, 0)'
},
alignedText: {
marginTop: Platform.OS === 'android' ? 4 : 6
},
selected: {
backgroundColor: appStyle.selectedDayBackgroundColor,
borderRadius: 16
},
todayText: {
color: appStyle.todayTextColor
},
selectedText: {
color: appStyle.selectedDayTextColor
},
disabledText: {
color: appStyle.textDisabledColor
},
...(theme[STYLESHEET_ID] || {})
});
}

module.exports = Day;
9 changes: 7 additions & 2 deletions src/calendar/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class CalendarHeader extends React.Component {
<TouchableOpacity
onPress={this.onPressLeft}
style={this.style.arrow}
hitSlop={{left: 20, right: 20, top: 20, bottom: 20}}
>
{this.props.renderArrow
? this.props.renderArrow('left')
Expand All @@ -102,7 +103,11 @@ class CalendarHeader extends React.Component {
</TouchableOpacity>
);
rightArrow = (
<TouchableOpacity onPress={this.onPressRight} style={this.style.arrow}>
<TouchableOpacity
onPress={this.onPressRight}
style={this.style.arrow}
hitSlop={{left: 20, right: 20, top: 20, bottom: 20}}
>
{this.props.renderArrow
? this.props.renderArrow('right')
: <Image
Expand Down Expand Up @@ -164,7 +169,7 @@ function styleConstructor(theme = {}, {rtl, type}) {
monthText: {
fontSize: appStyle.textMonthFontSize,
fontFamily: appStyle.textMonthFontFamily,
fontWeight: '300',
fontWeight: appStyle.textMonthFontWeight,
color: appStyle.monthTextColor,
margin: 10
},
Expand Down
3 changes: 3 additions & 0 deletions src/calendar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const { StyleSheet, View, ViewPropTypes } = require('react-native');
const Day = require('./day/basic');
const UnitDay = require('./day/period');
const MultiDotDay = require('./day/multi-dot');
const SingleDay = require('./day/custom');
const CalendarHeader = require('./header');
const shouldComponentUpdate = require('./updater');
const dateutils = require('../dateutils');
Expand Down Expand Up @@ -245,6 +246,8 @@ class Calendar extends React.Component {
return UnitDay;
case 'multi-dot':
return MultiDotDay;
case 'custom':
return SingleDay;
default:
return Day;
}
Expand Down
Loading

0 comments on commit d91e5a4

Please sign in to comment.