Skip to content

Commit

Permalink
Merge pull request wix#1559 from wix/ts-migration/calendar-list
Browse files Browse the repository at this point in the history
Ts migration/calendar list
  • Loading branch information
ethanshar authored Jul 5, 2021
2 parents 34ce810 + 41a398f commit dda11cd
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 89 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@babel/runtime": "^7.12.5",
"@types/lodash": "^4.14.170",
"@types/react-native": "^0.63.52",
"@types/xdate": "^0.8.32",
"@welldone-software/why-did-you-render": "^6.0.3",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.3",
Expand Down
161 changes: 90 additions & 71 deletions src/calendar-list/index.js → src/calendar-list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,80 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import XDate from 'xdate';

import React, {Component} from 'react';
import {FlatList, Platform, Dimensions, View} from 'react-native';

// @ts-expect-error
import {extractComponentProps} from '../component-updater';
// @ts-expect-error
import {xdateToData, parseDate} from '../interface';
// @ts-expect-error
import dateutils from '../dateutils';
// @ts-expect-error
import {STATIC_HEADER} from '../testIDs';
import styleConstructor from './style';
import Calendar from '../calendar';

// @ts-expect-error
import {CalendarProps} from '../calendar';
import CalendarListItem from './item';
// @ts-expect-error
import CalendarHeader from '../calendar/header/index';

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

export type CalendarListProps = CalendarProps & {
/** Max amount of months allowed to scroll to the past. Default = 50 */
pastScrollRange?: number;
/** Max amount of months allowed to scroll to the future. Default = 50 */
futureScrollRange?: number;
/** Used when calendar scroll is horizontal, default is device width, pagination should be disabled */
calendarWidth?: number;
/** Dynamic calendar height */
calendarHeight?: number;
/** Style for the List item (the calendar) */
calendarStyle?: number | Array<any> | Object;
/** Whether to use static header that will not scroll with the list (horizontal only) */
staticHeader?: boolean;
/** Enable or disable vertical / horizontal scroll indicator. Default = false */
showScrollIndicator?: boolean;
/** Whether to animate the auto month scroll */
animateScroll?: boolean;
/** Enable or disable scrolling of calendar list */
scrollEnabled?: boolean;
/** When true, the calendar list scrolls to top when the status bar is tapped. Default = true */
scrollsToTop?: boolean;
/** Enable or disable paging on scroll */
pagingEnabled?: boolean;
/** Whether the scroll is horizontal */
horizontal?: boolean;
/** Should Keyboard persist taps */
keyboardShouldPersistTaps?: 'never' | 'always' | 'handled';
/** A custom key extractor for the generated calendar months */
keyExtractor?: (item: any, index: number) => string;
/** How far from the end to trigger the onEndReached callback */
onEndReachedThreshold?: number;
/** Called once when the scroll position gets within onEndReachedThreshold */
onEndReached?: () => void;
};

type XDateAndBump = XDate & {propBump?: number} ;

type CalendarListState = {
rows: Array<XDateAndBump>;
texts: Array<string>;
openDate: XDate;
currentMonth: XDate;
}

/**
* @description: Calendar List component for both vertical and horizontal calendars
* @extends: Calendar
* @extendslink: docs/Calendar
* @example: https://github.com/wix/react-native-calendars/blob/master/example/src/screens/calendarsList.js
* @gif: https://github.com/wix/react-native-calendars/blob/master/demo/calendar-list.gif
*/
class CalendarList extends Component {
class CalendarList extends Component<CalendarListProps, CalendarListState> {
static displayName = 'CalendarList';

static propTypes = {
...Calendar.propTypes,
/** Max amount of months allowed to scroll to the past. Default = 50 */
pastScrollRange: PropTypes.number,
/** Max amount of months allowed to scroll to the future. Default = 50 */
futureScrollRange: PropTypes.number,
/** Used when calendar scroll is horizontal, default is device width, pagination should be disabled */
calendarWidth: PropTypes.number,
/** Dynamic calendar height */
calendarHeight: PropTypes.number,
/** Style for the List item (the calendar) */
calendarStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]),
/** Whether to use static header that will not scroll with the list (horizontal only) */
staticHeader: PropTypes.bool,
/** Enable or disable vertical / horizontal scroll indicator. Default = false */
showScrollIndicator: PropTypes.bool,
/** Whether to animate the auto month scroll */
animateScroll: PropTypes.bool,
/** Enable or disable scrolling of calendar list */
scrollEnabled: PropTypes.bool,
/** When true, the calendar list scrolls to top when the status bar is tapped. Default = true */
scrollsToTop: PropTypes.bool,
/** Enable or disable paging on scroll */
pagingEnabled: PropTypes.bool,
/** Whether the scroll is horizontal */
horizontal: PropTypes.bool,
/** Should Keyboard persist taps */
keyboardShouldPersistTaps: PropTypes.oneOf(['never', 'always', 'handled']),
/** A custom key extractor for the generated calendar months */
keyExtractor: PropTypes.func,
/** How far from the end to trigger the onEndReached callback */
onEndReachedThreshold: PropTypes.number,
/** Called once when the scroll position gets within onEndReachedThreshold */
onEndReached: PropTypes.func
};

static defaultProps = {
calendarWidth: width,
calendarHeight: 360,
Expand All @@ -72,21 +85,23 @@ class CalendarList extends Component {
scrollsToTop: false,
scrollEnabled: true,
removeClippedSubviews: Platform.OS === 'android',
keyExtractor: (item, index) => String(index)
keyExtractor: (_: any, index: number) => String(index)
};

style: any;
listView: FlatList<XDateAndBump> | undefined | null;
viewabilityConfig = {
itemVisiblePercentThreshold: 20
};

constructor(props) {
constructor(props: CalendarListProps) {
super(props);

this.style = styleConstructor(props.theme);

this.viewabilityConfig = {
itemVisiblePercentThreshold: 20
};

const rows = [];
const texts = [];
const date = parseDate(props.current) || XDate();
const date = parseDate(props.current) || new XDate();

for (let i = 0; i <= props.pastScrollRange + props.futureScrollRange; i++) {
const rangeDate = date.clone().addMonths(i - props.pastScrollRange, true);
Expand Down Expand Up @@ -114,7 +129,7 @@ class CalendarList extends Component {
};
}

componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: CalendarListProps) {
const prevCurrent = parseDate(prevProps.current);
const current = parseDate(this.props.current);

Expand All @@ -123,22 +138,24 @@ class CalendarList extends Component {
}
}

static getDerivedStateFromProps(nextProps, prevState) {
const rowclone = prevState.rows;
const newrows = [];

for (let i = 0; i < rowclone.length; i++) {
let val = prevState.texts[i];
if (rowclone[i].getTime) {
val = rowclone[i].clone();
val.propbump = rowclone[i].propbump ? rowclone[i].propbump + 1 : 1;
static getDerivedStateFromProps(_: CalendarListProps, prevState: CalendarListState) {
const rowClone = prevState.rows;
const newRows = [];

for (let i = 0; i < rowClone.length; i++) {
let val: XDate | string = prevState.texts[i];
// @ts-ignore
if (rowClone[i].getTime) {
val = rowClone[i].clone();
// @ts-ignore
val.propBump = rowClone[i].propBump ? rowClone[i].propBump + 1 : 1;
}
newrows.push(val);
newRows.push(val);
}
return {rows: newrows};
return {rows: newRows};
}

scrollToDay(d, offset, animated) {
scrollToDay(d: XDate, offset: number, animated: boolean) {
const {horizontal, calendarHeight, calendarWidth, pastScrollRange, firstDay} = this.props;
const day = parseDate(d);
const diffMonths = Math.round(this.state.openDate.clone().setDate(1).diffMonths(day.clone().setDate(1)));
Expand All @@ -156,21 +173,21 @@ class CalendarList extends Component {
}
}
}
this.listView.scrollToOffset({offset: scrollAmount, animated});
this.listView?.scrollToOffset({offset: scrollAmount, animated});
}

scrollToMonth = m => {
scrollToMonth = (m: XDate) => {
const {horizontal, calendarHeight, calendarWidth, pastScrollRange, animateScroll = false} = this.props;
const month = parseDate(m);
const scrollTo = month || this.state.openDate;
let diffMonths = Math.round(this.state.openDate.clone().setDate(1).diffMonths(scrollTo.clone().setDate(1)));
const size = horizontal ? calendarWidth : calendarHeight;
const scrollAmount = size * pastScrollRange + diffMonths * size;

this.listView.scrollToOffset({offset: scrollAmount, animated: animateScroll});
this.listView?.scrollToOffset({offset: scrollAmount, animated: animateScroll});
};

getItemLayout = (data, index) => {
getItemLayout = (_: Array<XDateAndBump> | undefined | null, index: number) => {
const {horizontal, calendarHeight, calendarWidth} = this.props;

return {
Expand All @@ -180,16 +197,16 @@ class CalendarList extends Component {
};
};

getMonthIndex(month) {
getMonthIndex(month: XDate) {
let diffMonths = this.state.openDate.diffMonths(month) + this.props.pastScrollRange;
return diffMonths;
}

addMonth = count => {
addMonth = (count: number) => {
this.updateMonth(this.state.currentMonth.clone().addMonths(count, true));
};

updateMonth(day, doNotTriggerListeners) {
updateMonth(day: XDate, doNotTriggerListeners = false) {
if (day.toString('yyyy MM') === this.state.currentMonth.toString('yyyy MM')) {
return;
}
Expand All @@ -206,8 +223,8 @@ class CalendarList extends Component {
});
}

onViewableItemsChanged = ({viewableItems}) => {
function rowIsCloseToViewable(index, distance) {
onViewableItemsChanged = ({viewableItems}: any) => {
function rowIsCloseToViewable(index: number, distance: number) {
for (let i = 0; i < viewableItems.length; i++) {
if (Math.abs(index - parseInt(viewableItems[i].index)) <= distance) {
return true;
Expand All @@ -221,7 +238,7 @@ class CalendarList extends Component {
const visibleMonths = [];

for (let i = 0; i < rowclone.length; i++) {
let val = rowclone[i];
let val: XDate | string = rowclone[i];
const rowShouldBeRendered = rowIsCloseToViewable(i, 1);

if (rowShouldBeRendered && !rowclone[i].getTime) {
Expand All @@ -238,12 +255,13 @@ class CalendarList extends Component {
_.invoke(this.props, 'onVisibleMonthsChange', visibleMonths);

this.setState({
// @ts-ignore
rows: newrows,
currentMonth: parseDate(visibleMonths[0])
});
};

renderItem = ({item}) => {
renderItem = ({item}: any) => {
const {calendarStyle, horizontal, calendarWidth, testID, ...others} = this.props;

return (
Expand Down Expand Up @@ -287,6 +305,7 @@ class CalendarList extends Component {
<FlatList
ref={c => (this.listView = c)}
style={[this.style.container, style]}
// @ts-ignore
initialListSize={pastScrollRange + futureScrollRange + 1} // ListView deprecated
data={this.state.rows}
renderItem={this.renderItem}
Expand Down
42 changes: 25 additions & 17 deletions src/calendar-list/item.js → src/calendar-list/item.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,51 @@
import PropTypes from 'prop-types';
import memoize from 'memoize-one';

import React, {Component} from 'react';
import {Text, View} from 'react-native';

// @ts-expect-error
import {extractComponentProps} from '../component-updater';
import Calendar from '../calendar';

// @ts-expect-error
import Calendar, {CalendarProps} from '../calendar';
import styleConstructor from './style';

class CalendarListItem extends Component {
static displayName = 'IGNORE';
export type CalendarListItemProps = CalendarProps & {
item: any;
calendarWidth?: number;
calendarHeight?: number;
horizontal?: boolean;
theme?: any;
}

static propTypes = {
...Calendar.propTypes,
item: PropTypes.any,
calendarWidth: PropTypes.number,
calendarHeight: PropTypes.number,
horizontal: PropTypes.bool
};
type CalendarListItemState = {
hideArrows: boolean;
hideExtraDays: boolean;
}

class CalendarListItem extends Component<CalendarListItemProps, CalendarListItemState> {
static displayName = 'IGNORE';

static defaultProps = {
hideArrows: true,
hideExtraDays: true
};

constructor(props) {
style: any;

constructor(props: CalendarListItemProps) {
super(props);

this.style = styleConstructor(props.theme);
}

shouldComponentUpdate(nextProps) {
shouldComponentUpdate(nextProps: CalendarListItemProps) {
const r1 = this.props.item;
const r2 = nextProps.item;

return r1.toString('yyyy MM') !== r2.toString('yyyy MM') || !!(r2.propbump && r2.propbump !== r1.propbump);
return r1.toString('yyyy MM') !== r2.toString('yyyy MM') || !!(r2.propBump && r2.propBump !== r1.propBump);
}

onPressArrowLeft = (_, month) => {
onPressArrowLeft = (_: any, month: any) => {
const {onPressArrowLeft, scrollToMonth} = this.props;
const monthClone = month.clone();

Expand All @@ -56,7 +64,7 @@ class CalendarListItem extends Component {
}
};

onPressArrowRight = (_, month) => {
onPressArrowRight = (_: any, month: any) => {
const {onPressArrowRight, scrollToMonth} = this.props;
const monthClone = month.clone();

Expand Down
3 changes: 2 additions & 1 deletion src/calendar-list/style.js → src/calendar-list/style.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Platform, StyleSheet} from 'react-native';
// @ts-expect-error
import * as defaultStyle from '../style';

const STYLESHEET_ID = 'stylesheet.calendar-list.main';

export default function getStyle(theme = {}) {
export default function getStyle(theme: any = {}) {
const appStyle = {...defaultStyle, ...theme};
return StyleSheet.create({
flatListContainer: {
Expand Down

0 comments on commit dda11cd

Please sign in to comment.