Skip to content

Commit

Permalink
Feature: RTL (hoangnm#52)
Browse files Browse the repository at this point in the history
* Minor fixes

- Reuse code
- Avoid extra state.initialDates change

* Add prependMostRecent prop

* Add rightToLeft prop
  • Loading branch information
pdpino authored Oct 30, 2020
1 parent b870fcc commit 5b40145
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 45 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
* `startHour` _(Number)_ - hour clicked (as integer)
* `date` _(Date)_ - date object indicating day clicked (the hour is not relevant)
* **`EventComponent`** _(React.Component)_ - Component rendered inside an event. By default, is a `Text` with the `event.description`. See below for details on the component.
* **`rightToLeft`** _(Boolean)_ - If true, render older days to the right and more recent days to the left.
* **`prependMostRecent`** _(Boolean)_ - If true, the horizontal prepending is done in the most recent dates. See [issue #39](https://github.com/hoangnm/react-native-week-view/issues/39) for more details. Default is false.

## Event Object
```js
Expand All @@ -36,9 +38,9 @@
```

## Custom `EventComponent`
The component will be rendered inside a `TouchableOpacity`, which has the backgroud color set to `event.color`, and is placed with absolute position in the grid. The component receives two props:
The component will be rendered inside a `TouchableOpacity`, which has the background color set to `event.color`, and is placed with absolute position in the grid. The component receives two props:
* **`event`** _(Event)_ - Event object as described before.
* **`position`**: _(Object)_ - object containint `top`, `left`, `height` and `width` values in pixels.
* **`position`**: _(Object)_ - object containing `top`, `left`, `height` and `width` values in pixels.

For example, to display an icon inside each event, such as a [react-native-elements Icon](https://react-native-elements.github.io/react-native-elements/docs/icon/):
```js
Expand Down
29 changes: 17 additions & 12 deletions src/Events/Events.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,20 @@ class Events extends PureComponent {
return EVENTS_CONTAINER_WIDTH / numberOfDays;
};

processEvents = memoizeOne((eventsByDate, initialDate, numberOfDays) => {
// totalEvents stores events in each day of numberOfDays
// example: [[event1, event2], [event3, event4], [event5]], each child array
// is events for specific day in range
const dates = calculateDaysArray(initialDate, numberOfDays);
const totalEvents = dates.map((date) => {
const dateStr = date.format(DATE_STR_FORMAT);
return eventsByDate[dateStr] || [];
});
const totalEventsWithPosition = this.getEventsWithPosition(totalEvents);
return totalEventsWithPosition;
});
processEvents = memoizeOne(
(eventsByDate, initialDate, numberOfDays, rightToLeft) => {
// totalEvents stores events in each day of numberOfDays
// example: [[event1, event2], [event3, event4], [event5]], each child array
// is events for specific day in range
const dates = calculateDaysArray(initialDate, numberOfDays, rightToLeft);
const totalEvents = dates.map((date) => {
const dateStr = date.format(DATE_STR_FORMAT);
return eventsByDate[dateStr] || [];
});
const totalEventsWithPosition = this.getEventsWithPosition(totalEvents);
return totalEventsWithPosition;
},
);

onGridClick = (event, dayIndex) => {
const { initialDate, onGridClick } = this.props;
Expand All @@ -158,11 +160,13 @@ class Events extends PureComponent {
onEventPress,
eventContainerStyle,
EventComponent,
rightToLeft,
} = this.props;
const totalEvents = this.processEvents(
eventsByDate,
initialDate,
numberOfDays,
rightToLeft,
);

return (
Expand Down Expand Up @@ -212,6 +216,7 @@ Events.propTypes = {
onGridClick: PropTypes.func,
eventContainerStyle: PropTypes.object,
EventComponent: PropTypes.elementType,
rightToLeft: PropTypes.bool,
};

export default Events;
4 changes: 3 additions & 1 deletion src/Header/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ const WeekViewHeader = ({
formatDate,
style,
textStyle,
rightToLeft,
}) => {
const columns = calculateDaysArray(initialDate, numberOfDays);
const columns = calculateDaysArray(initialDate, numberOfDays, rightToLeft);
return (
<View style={styles.container}>
{columns && (
Expand All @@ -80,6 +81,7 @@ WeekViewHeader.propTypes = {
formatDate: PropTypes.string,
style: PropTypes.object,
textStyle: PropTypes.object,
rightToLeft: PropTypes.bool,
};

WeekViewHeader.defaultProps = {
Expand Down
85 changes: 57 additions & 28 deletions src/WeekView/WeekView.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default class WeekView extends Component {
initialDates: this.calculatePagesDates(
props.selectedDate,
props.numberOfDays,
props.prependMostRecent,
),
};

Expand Down Expand Up @@ -94,7 +95,12 @@ export default class WeekView extends Component {
} = event;
const { x: position } = contentOffset;
const { width: innerWidth } = contentSize;
const { onSwipePrev, onSwipeNext, numberOfDays } = this.props;
const {
onSwipePrev,
onSwipeNext,
numberOfDays,
prependMostRecent,
} = this.props;
const { currentMoment, initialDates } = this.state;

const newPage = Math.round((position / innerWidth) * initialDates.length);
Expand All @@ -106,32 +112,41 @@ export default class WeekView extends Component {
}

InteractionManager.runAfterInteractions(() => {
const daySignToTheFuture = prependMostRecent ? -1 : 1;
const newMoment = moment(currentMoment)
.add(movedPages * numberOfDays, 'd')
.add(movedPages * numberOfDays * daySignToTheFuture, 'd')
.toDate();

if (movedPages < 0 && newPage < 2) {
const newState = {
currentMoment: newMoment,
};

if (movedPages < 0 && newPage < this.pageOffset) {
const first = initialDates[0];
const initialDate = moment(first).add(-numberOfDays, 'd');
const daySignToThePast = daySignToTheFuture * -1;
const addDays = numberOfDays * daySignToThePast;
const initialDate = moment(first).add(addDays, 'd');
initialDates.unshift(initialDate.format(DATE_STR_FORMAT));
this.currentPageIndex += 1;
this.eventsGrid.scrollToIndex({
index: this.currentPageIndex,
animated: false,
});

newState.initialDates = [...initialDates];
} else if (
movedPages > 0 &&
newPage > this.state.initialDates.length - 2
newPage > this.state.initialDates.length - this.pageOffset
) {
const latest = initialDates[initialDates.length - 1];
const initialDate = moment(latest).add(numberOfDays, 'd');
const addDays = numberOfDays * daySignToTheFuture;
const initialDate = moment(latest).add(addDays, 'd');
initialDates.push(initialDate.format(DATE_STR_FORMAT));

newState.initialDates = [...initialDates];
}

this.setState({
initialDates: [...initialDates],
currentMoment: newMoment,
});
this.setState(newState);

if (movedPages < 0) {
onSwipePrev && onSwipePrev(newMoment);
Expand All @@ -153,14 +168,16 @@ export default class WeekView extends Component {
this.header = ref;
};

calculatePagesDates = memoizeOne((currentMoment, numberOfDays) => {
const initialDates = [];
for (let i = -this.pageOffset; i <= this.pageOffset; i += 1) {
const initialDate = moment(currentMoment).add(numberOfDays * i, 'd');
initialDates.push(initialDate.format(DATE_STR_FORMAT));
}
return initialDates;
});
calculatePagesDates = memoizeOne(
(currentMoment, numberOfDays, prependMostRecent) => {
const initialDates = [];
for (let i = -this.pageOffset; i <= this.pageOffset; i += 1) {
const initialDate = moment(currentMoment).add(numberOfDays * i, 'd');
initialDates.push(initialDate.format(DATE_STR_FORMAT));
}
return prependMostRecent ? initialDates.reverse() : initialDates;
},
);

sortEventsByDate = memoizeOne((events) => {
// Stores the events hashed by their date
Expand Down Expand Up @@ -203,6 +220,12 @@ export default class WeekView extends Component {
return sortedEvents;
});

getListItemLayout = (index) => ({
length: CONTAINER_WIDTH,
offset: CONTAINER_WIDTH * index,
index,
});

render() {
const {
showTitle,
Expand All @@ -217,10 +240,16 @@ export default class WeekView extends Component {
hoursInDisplay,
onGridClick,
EventComponent,
prependMostRecent,
rightToLeft,
} = this.props;
const { currentMoment, initialDates } = this.state;
const times = this.calculateTimes(hoursInDisplay);
const eventsByDate = this.sortEventsByDate(events);
const horizontalInverted =
(prependMostRecent && !rightToLeft) ||
(!prependMostRecent && rightToLeft);

return (
<View style={styles.container}>
<View style={styles.headerContainer}>
Expand All @@ -234,17 +263,14 @@ export default class WeekView extends Component {
<VirtualizedList
horizontal
pagingEnabled
inverted={horizontalInverted}
showsHorizontalScrollIndicator={false}
scrollEnabled={false}
ref={this.headerRef}
data={initialDates}
getItem={(data, index) => data[index]}
getItemCount={(data) => data.length}
getItemLayout={(_, index) => ({
length: CONTAINER_WIDTH,
offset: CONTAINER_WIDTH * index,
index,
})}
getItemLayout={(_, index) => this.getListItemLayout(index)}
keyExtractor={(item) => item}
initialScrollIndex={this.pageOffset}
renderItem={({ item }) => {
Expand All @@ -256,6 +282,7 @@ export default class WeekView extends Component {
formatDate={formatDateHeader}
initialDate={item}
numberOfDays={numberOfDays}
rightToLeft={rightToLeft}
/>
</View>
);
Expand All @@ -269,11 +296,7 @@ export default class WeekView extends Component {
data={initialDates}
getItem={(data, index) => data[index]}
getItemCount={(data) => data.length}
getItemLayout={(_, index) => ({
length: CONTAINER_WIDTH,
offset: CONTAINER_WIDTH * index,
index,
})}
getItemLayout={(_, index) => this.getListItemLayout(index)}
keyExtractor={(item) => item}
initialScrollIndex={this.pageOffset}
renderItem={({ item }) => {
Expand All @@ -288,11 +311,13 @@ export default class WeekView extends Component {
hoursInDisplay={hoursInDisplay}
EventComponent={EventComponent}
eventContainerStyle={eventContainerStyle}
rightToLeft={rightToLeft}
/>
);
}}
horizontal
pagingEnabled
inverted={horizontalInverted}
onMomentumScrollEnd={this.scrollEnded}
scrollEventThrottle={32}
onScroll={Animated.event(
Expand Down Expand Up @@ -334,6 +359,8 @@ WeekView.propTypes = {
startHour: PropTypes.number,
EventComponent: PropTypes.elementType,
showTitle: PropTypes.bool,
rightToLeft: PropTypes.bool,
prependMostRecent: PropTypes.bool,
};

WeekView.defaultProps = {
Expand All @@ -342,4 +369,6 @@ WeekView.defaultProps = {
hoursInDisplay: 6,
startHour: 0,
showTitle: true,
rightToLeft: false,
prependMostRecent: false,
};
4 changes: 2 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const getCurrentMonth = (date) => {
return moment(date).format('MMMM Y');
};

export const calculateDaysArray = (date, numberOfDays) => {
export const calculateDaysArray = (date, numberOfDays, rightToLeft) => {
const dates = [];
let initial = 0;
if (numberOfDays === 7) {
Expand All @@ -38,5 +38,5 @@ export const calculateDaysArray = (date, numberOfDays) => {
const currentDate = moment(date).add(i, 'd');
dates.push(currentDate);
}
return dates;
return rightToLeft ? dates.reverse() : dates;
};

0 comments on commit 5b40145

Please sign in to comment.