@momo-kits/calendar 0.0.48-rc.2 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MonthList.tsx ADDED
@@ -0,0 +1,282 @@
1
+ import React, {Component} from 'react';
2
+ import {FlatList, Platform} from 'react-native';
3
+ import moment from 'moment';
4
+ import Moment from 'moment';
5
+ import Month from './Month';
6
+ import LunarDateConverter from './LunarDateConverter';
7
+ import {Holidays, MonthListProps, MonthListState} from './types';
8
+ import {ContainerContext} from './index';
9
+ import {scaleSize, Spacing} from '@momo-kits/foundation';
10
+
11
+ const MAX_RENDER_PER_BATCH = Platform.OS === 'android' ? 1 : 12;
12
+
13
+ // @ts-ignore
14
+ const converter = new LunarDateConverter();
15
+ export default class MonthList extends Component<
16
+ MonthListProps,
17
+ MonthListState
18
+ > {
19
+ currentDate;
20
+ data;
21
+ currentScrollIndex;
22
+ list;
23
+ heightStyle;
24
+ holidays?: Holidays[] = [];
25
+ currentKey: any;
26
+
27
+ constructor(props: MonthListProps) {
28
+ super(props);
29
+ this.currentDate = moment();
30
+ this.data = this.getMonthList();
31
+ this.currentScrollIndex = this.getIndexOfMonth(
32
+ moment(props.selectedDate),
33
+ this.data,
34
+ );
35
+ this.list = React.createRef<FlatList>();
36
+ this.heightStyle = {};
37
+ this.holidays = props.holidays;
38
+ }
39
+
40
+ renderMonth = (data: {
41
+ item: {dateList: any; date: moment.Moment};
42
+ index: number;
43
+ }) => {
44
+ const {isDoubleDateMode, disabledDays, priceList, isShowLunar} = this.props;
45
+ const {item, index} = data;
46
+
47
+ const keyMonth = moment(item.date)?.format('YYYY-MM');
48
+ const priceListDate = priceList?.[keyMonth];
49
+
50
+ return (
51
+ <Month
52
+ {...this.props}
53
+ key={index}
54
+ month={item.date || {}}
55
+ dateList={item.dateList || []}
56
+ priceListDate={priceListDate}
57
+ isShowLunar={isShowLunar}
58
+ isDoubleDateMode={isDoubleDateMode}
59
+ disabledDays={disabledDays}
60
+ />
61
+ );
62
+ };
63
+
64
+ checkRange = (
65
+ date?: moment.Moment | null,
66
+ start?: moment.Moment | null,
67
+ end?: moment.Moment | null,
68
+ ) => {
69
+ if (!date || !start) return false;
70
+ if (!end) {
71
+ return date.year() === start.year() && date.month() === start.month();
72
+ }
73
+ if (
74
+ date.year() < start.year() ||
75
+ (date.year() === start.year() && date.month() < start.month())
76
+ ) {
77
+ return false;
78
+ }
79
+ return !(
80
+ date.year() > end.year() ||
81
+ (date.year() === end.year() && date.month() > end.month())
82
+ );
83
+ };
84
+
85
+ shouldUpdate = (
86
+ month: {date: any; dateList?: any[]},
87
+ props?: MonthListProps,
88
+ ) => {
89
+ if (!props) return false;
90
+ const {startDate, endDate} = props;
91
+ const {startDate: curStartDate, endDate: curEndDate} = this.props;
92
+ const {date} = month;
93
+ const next = this.checkRange(date, startDate, endDate);
94
+ const prev = this.checkRange(date, curStartDate, curEndDate);
95
+ return prev || next;
96
+ };
97
+
98
+ convertLunarToSolar(date: moment.Moment) {
99
+ return date
100
+ ? converter.SolarToLunar({
101
+ solarDay: date.date(),
102
+ solarMonth: date.month() + 1,
103
+ solarYear: date.year(),
104
+ })
105
+ : {};
106
+ }
107
+
108
+ findHoliday = (date: Moment.Moment) => {
109
+ const {holidays} = this.props;
110
+ if (date && holidays && holidays.length > 0) {
111
+ const day = date.date();
112
+ const month = date.month() + 1;
113
+ return holidays.find(item => item.day === day && item.month === month);
114
+ }
115
+ return null;
116
+ };
117
+
118
+ checkHoliday = (date: moment.Moment) => {
119
+ const holiday = this.findHoliday(date);
120
+ return {
121
+ isSolarHoliday: !!(holiday && !holiday.lunar),
122
+ isLunarHoliday: !!(holiday && holiday.lunar),
123
+ };
124
+ };
125
+
126
+ getDayList = (date: moment.Moment) => {
127
+ let dayList;
128
+ const month = date.month();
129
+ let weekday = date.isoWeekday();
130
+ if (weekday === 1) {
131
+ dayList = [];
132
+ } else {
133
+ dayList = new Array(weekday - 1).fill({
134
+ empty: moment(date).subtract(1, 'h'),
135
+ lunarDate: this.convertLunarToSolar(date),
136
+ ...this.checkHoliday(date),
137
+ });
138
+ }
139
+ while (date.month() === month) {
140
+ const cloned = moment(date);
141
+ dayList.push({
142
+ date: cloned,
143
+ lunarDate: this.convertLunarToSolar(cloned),
144
+ ...this.checkHoliday(date),
145
+ });
146
+ date.add(1, 'days');
147
+ }
148
+ date.subtract(1, 'days');
149
+ weekday = date.isoWeekday();
150
+ if (weekday === 1) {
151
+ return dayList.concat(
152
+ new Array(6).fill({
153
+ empty: moment(date).hour(1),
154
+ lunarDate: this.convertLunarToSolar(date),
155
+ }),
156
+ );
157
+ }
158
+ return dayList.concat(
159
+ new Array(Math.abs(7 - weekday)).fill({
160
+ empty: moment(date).hour(1),
161
+ lunarDate: this.convertLunarToSolar(date),
162
+ }),
163
+ );
164
+ };
165
+
166
+ getMonthList = (props?: MonthListProps) => {
167
+ const minDate = moment((props || this.props).minDate).date(1);
168
+ const maxDate = moment((props || this.props).maxDate);
169
+ const monthList: {date: moment.Moment; dateList: any[]}[] = [];
170
+ if (!maxDate || !minDate) return monthList;
171
+ while (
172
+ maxDate > minDate ||
173
+ (maxDate.year() === minDate.year() && maxDate.month() === minDate.month())
174
+ ) {
175
+ const d = moment(minDate);
176
+ const month = {
177
+ date: d,
178
+ dateList: this.getDayList(d),
179
+ shouldUpdate: false,
180
+ };
181
+ month.shouldUpdate = this.shouldUpdate(month, props);
182
+ monthList.push(month);
183
+ minDate.add(1, 'month');
184
+ }
185
+
186
+ return monthList;
187
+ };
188
+
189
+ getIndexOfMonth = (month: moment.Moment, data: any[]) => {
190
+ return data.findIndex(item => item.date.isSame(month, 'month'));
191
+ };
192
+
193
+ getCurrentVisibleMonth = (info: {changed: any}) => {
194
+ const {changed} = info;
195
+ const {onScrollCalendar} = this.props;
196
+ if (changed && changed.length > 0) {
197
+ const {item, key, index} = changed[0];
198
+ if (onScrollCalendar && item && this.currentKey !== key) {
199
+ this.currentKey = key;
200
+ try {
201
+ this.heightStyle =
202
+ this.data && this.data[index] && this.data[index].dateList
203
+ ? {
204
+ height:
205
+ (scaleSize(48) * this.data[index].dateList.length) / 7 +
206
+ Spacing.L * 2 +
207
+ Spacing.XS,
208
+ }
209
+ : {};
210
+ } catch (e) {
211
+ this.heightStyle = {};
212
+ }
213
+ if (onScrollCalendar) {
214
+ onScrollCalendar({
215
+ date: item.date,
216
+ key,
217
+ currentIndex: index,
218
+ });
219
+ }
220
+ }
221
+ }
222
+ };
223
+
224
+ keyExtractor = (item: {date: Moment.Moment}) =>
225
+ `${item.date.month() + 1}/${item.date.year()}`;
226
+
227
+ scrollToMonth = (month: moment.Moment) => {
228
+ const index = this.getIndexOfMonth(month, this.data);
229
+ if (this.list.current && index !== -1) {
230
+ this.list.current.scrollToIndex({
231
+ index,
232
+ animated: true,
233
+ });
234
+ }
235
+ };
236
+
237
+ getItemLayout = (data: any, index: number, width: number) => ({
238
+ length: width,
239
+ offset: width * index,
240
+ index,
241
+ });
242
+
243
+ componentDidMount() {
244
+ setTimeout(() => {
245
+ this.scrollToMonth(this.currentDate);
246
+ }, 500);
247
+ }
248
+
249
+ render() {
250
+ const {priceList} = this.props;
251
+ return (
252
+ <ContainerContext.Consumer>
253
+ {size => (
254
+ <FlatList
255
+ extraData={priceList}
256
+ style={this.heightStyle}
257
+ pagingEnabled
258
+ horizontal
259
+ ref={this.list}
260
+ data={this.data}
261
+ renderItem={this.renderMonth}
262
+ showsHorizontalScrollIndicator={false}
263
+ keyExtractor={this.keyExtractor}
264
+ onViewableItemsChanged={this.getCurrentVisibleMonth}
265
+ onScrollToIndexFailed={() => {}}
266
+ scrollEnabled
267
+ initialNumToRender={3}
268
+ maxToRenderPerBatch={MAX_RENDER_PER_BATCH}
269
+ windowSize={1}
270
+ initialScrollIndex={this.currentScrollIndex}
271
+ getItemLayout={(data, index) =>
272
+ this.getItemLayout(data, index, size.width)
273
+ }
274
+ viewabilityConfig={{
275
+ itemVisiblePercentThreshold: 50,
276
+ }}
277
+ />
278
+ )}
279
+ </ContainerContext.Consumer>
280
+ );
281
+ }
282
+ }
package/TabHeader.tsx ADDED
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import {TouchableWithoutFeedback, View} from 'react-native';
3
+ import Moment from 'moment';
4
+ import Util from './Util';
5
+ import {
6
+ ApplicationContext,
7
+ Colors,
8
+ Radius,
9
+ Spacing,
10
+ Text,
11
+ } from '@momo-kits/foundation';
12
+ import {TabHeaderProps, TabHeaderState} from './types';
13
+
14
+ export default class TabHeader extends React.Component<
15
+ TabHeaderProps,
16
+ TabHeaderState
17
+ > {
18
+ static contextType = ApplicationContext;
19
+ label;
20
+ defaultDate: Moment.Moment;
21
+
22
+ constructor(props: TabHeaderProps) {
23
+ super(props);
24
+ this.state = {
25
+ active: props.activeTab,
26
+ };
27
+ this.label = props.label;
28
+ this.defaultDate = props.date ? Moment(props.date) : Moment();
29
+ }
30
+
31
+ onChangeTab = () => {
32
+ const {onChangeTab, id} = this.props;
33
+ onChangeTab?.(id);
34
+ };
35
+
36
+ updateView = (date?: Moment.Moment | Date | null, activeTab?: boolean) => {
37
+ this.setState({
38
+ date: date ? Moment(date) : Moment(),
39
+ active: activeTab,
40
+ });
41
+ };
42
+
43
+ setActiveTab = (active: boolean) => {
44
+ this.setState({active});
45
+ };
46
+
47
+ render() {
48
+ const {label, disabled} = this.props;
49
+ const {date, active} = this.state;
50
+ const {theme, translate} = this.context;
51
+
52
+ const formattedDateFromState = date ? date.format('DD/MM/YYYY') : '';
53
+ const formattedDateFromDefault = this.defaultDate
54
+ ? this.defaultDate.format('DD/MM/YYYY')
55
+ : '';
56
+ const dayOfWeekFromState = date
57
+ ? `${translate(Util.WEEKDAYSFROMMOMENT[date.day()])} -`
58
+ : '';
59
+ const dayOfWeekFromDefault = this.defaultDate
60
+ ? `${translate(Util.WEEKDAYSFROMMOMENT[this.defaultDate.day()])} -`
61
+ : '';
62
+
63
+ return (
64
+ <TouchableWithoutFeedback disabled={disabled} onPress={this.onChangeTab}>
65
+ <View
66
+ style={[
67
+ {
68
+ flex: 1,
69
+ padding: Spacing.S,
70
+ borderRadius: Radius.S,
71
+ },
72
+ active &&
73
+ !disabled && {
74
+ backgroundColor: theme.colors.background.surface,
75
+ },
76
+ ]}>
77
+ <Text
78
+ typography={'description_default_regular'}
79
+ color={Colors.black_12}>
80
+ {translate(label)}
81
+ </Text>
82
+ <Text typography={'header_s_semibold'}>
83
+ {`${dayOfWeekFromState || dayOfWeekFromDefault || '--'} `}
84
+ {formattedDateFromState || formattedDateFromDefault || '--/--/----'}
85
+ </Text>
86
+ </View>
87
+ </TouchableWithoutFeedback>
88
+ );
89
+ }
90
+ }
package/Util.ts ADDED
@@ -0,0 +1,293 @@
1
+ import moment from 'moment';
2
+ import LunarDateConverter from './LunarDateConverter';
3
+ import holiday from './holidayData';
4
+
5
+ const I18N_MAP = {
6
+ en: {
7
+ w: ['', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun'],
8
+ weekday: [
9
+ '',
10
+ 'Monday',
11
+ 'Tuesday',
12
+ 'Wednesday',
13
+ 'Thursday',
14
+ 'Friday',
15
+ 'Saturday',
16
+ 'Sunday',
17
+ ],
18
+ text: {
19
+ start: 'Start',
20
+ end: 'End',
21
+ date: 'Date',
22
+ save: 'Save',
23
+ clear: 'Reset',
24
+ },
25
+ date: 'DD / MM',
26
+ },
27
+ vi: {
28
+ w: ['', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
29
+ weekday: [
30
+ '',
31
+ 'Thứ hai',
32
+ 'Thứ ba',
33
+ 'Thứ tư',
34
+ 'Thứ năm',
35
+ 'Thứ sáu',
36
+ 'Thứ bảy',
37
+ 'Chủ nhật',
38
+ ],
39
+ text: {
40
+ start: 'Start',
41
+ end: 'End',
42
+ date: 'Date',
43
+ save: 'Save',
44
+ clear: 'Reset',
45
+ },
46
+ date: 'DD / MM',
47
+ },
48
+ };
49
+
50
+ const Solar = (momentDate: moment.Moment) => ({
51
+ solarDay: momentDate.date(),
52
+ solarMonth: momentDate.month() + 1,
53
+ solarYear: momentDate.year(),
54
+ });
55
+
56
+ const formatYYYYMMDD = (dd: number, mm: number, yyyy: any) =>
57
+ `${yyyy}-${mm < 10 ? `0${mm}` : mm}-${dd < 10 ? `0${dd}` : dd}`;
58
+
59
+ const formatDDMM = (dd: number, mm: number) =>
60
+ `${dd < 10 ? `0${dd}` : dd}/${mm < 10 ? `0${mm}` : mm}`;
61
+
62
+ const groupHolidaysByDate = (holidays: any[]) => {
63
+ const groupedHolidays: {[key: string]: string[]} = {};
64
+ if (holidays && holidays.length > 0) {
65
+ holidays.forEach(item => {
66
+ const {day, month, lunar, label} = item;
67
+ const key = formatDDMM(day, month);
68
+ let holidaysObj = groupedHolidays[key]
69
+ ? {...groupedHolidays[key]}
70
+ : {...item};
71
+ if (groupedHolidays[key]) {
72
+ if (holidaysObj.lunar && !lunar) {
73
+ holidaysObj = {
74
+ ...holidaysObj,
75
+ label,
76
+ mixedLabel: `${label}, ${holidaysObj.label}`,
77
+ };
78
+ } else if (!holidaysObj.lunar && lunar) {
79
+ holidaysObj = {
80
+ ...holidaysObj,
81
+ label: holidaysObj.lunar,
82
+ mixedLabel: `${holidaysObj.lunar}, ${label}`,
83
+ };
84
+ }
85
+ }
86
+ groupedHolidays[key] = holidaysObj;
87
+ });
88
+ }
89
+
90
+ return groupedHolidays;
91
+ };
92
+
93
+ const sortByDate = (arr: any[]) => {
94
+ if (arr && arr.length > 1) {
95
+ arr.sort((a, b) => {
96
+ if (a.month > b.month || (a.month === b.month && a.day > b.day)) {
97
+ return 1;
98
+ }
99
+ return -1;
100
+ });
101
+ }
102
+
103
+ return groupHolidaysByDate(arr);
104
+ };
105
+
106
+ const Utils = {
107
+ WEEKDAYS: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
108
+
109
+ WEEKDAYSFROMMOMENT: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
110
+
111
+ WEEKDAYSNAME: [
112
+ 'Thứ 2',
113
+ 'Thứ 3',
114
+ 'Thứ 4',
115
+ 'Thứ 5',
116
+ 'Thứ 6',
117
+ 'Thứ 7',
118
+ 'Chủ nhật',
119
+ ],
120
+
121
+ MONTHS: [
122
+ 'Tháng 1',
123
+ 'Tháng 2',
124
+ 'Tháng 3',
125
+ 'Tháng 4',
126
+ 'Tháng 5',
127
+ 'Tháng 6',
128
+ 'Tháng 7',
129
+ 'Tháng 8',
130
+ 'Tháng 9',
131
+ 'Tháng 10',
132
+ 'Tháng 11',
133
+ 'Tháng 12',
134
+ ],
135
+
136
+ MAX_ROWS: 7,
137
+
138
+ MAX_COLUMNS: 7,
139
+
140
+ mapWeeKDate(i: number) {
141
+ const date = ['', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
142
+ return date[i];
143
+ },
144
+
145
+ mapMonth(i: number) {
146
+ const month = new Map([
147
+ [1, 'jan'],
148
+ [2, 'feb'],
149
+ [3, 'mar'],
150
+ [4, 'apr'],
151
+ [5, 'may'],
152
+ [6, 'jun'],
153
+ [7, 'jul'],
154
+ [8, 'aug'],
155
+ [9, 'sep'],
156
+ [10, 'oct'],
157
+ [11, 'nov'],
158
+ [12, 'dec'],
159
+ ]);
160
+ return month.get(i);
161
+ },
162
+
163
+ mapMonthShorten(i: number) {
164
+ const month = new Map([
165
+ [1, 'Jan'],
166
+ [2, 'Feb'],
167
+ [3, 'Mar'],
168
+ [4, 'Apr'],
169
+ [5, 'May'],
170
+ [6, 'Jun'],
171
+ [7, 'Jul'],
172
+ [8, 'Aug'],
173
+ [9, 'Sep'],
174
+ [10, 'Oct'],
175
+ [11, 'Nov'],
176
+ [12, 'Dec'],
177
+ ]);
178
+ return month.get(i);
179
+ },
180
+
181
+ getDaysInMonth(month: number, year: number) {
182
+ const lastDayOfMonth = new Date(year, month + 1, 0);
183
+ return lastDayOfMonth.getDate();
184
+ },
185
+
186
+ getHolidaysInMonth(headerInfo: moment.Moment) {
187
+ if (headerInfo) {
188
+ const today = moment();
189
+ // @ts-ignore
190
+ const converter = new LunarDateConverter();
191
+ const startDate = moment(headerInfo).startOf('month');
192
+ const endDate = moment(headerInfo).endOf('month');
193
+ const minLunarDate = converter.SolarToLunar(Solar(startDate));
194
+ const maxLunarDate = converter.SolarToLunar(Solar(endDate));
195
+ const holidays: any[] = [];
196
+ const currentYear =
197
+ minLunarDate.lunarYear !== maxLunarDate.lunarYear
198
+ ? [minLunarDate.lunarYear, maxLunarDate.lunarYear]
199
+ : minLunarDate.lunarYear;
200
+
201
+ // Handle Solar holidays
202
+ // @ts-ignore
203
+ if (holiday.solar[headerInfo.month() + 1]) {
204
+ // @ts-ignore
205
+ holiday.solar[headerInfo.month() + 1].forEach(
206
+ (date: {month: number; day: any}) => {
207
+ const dateAsMoment = moment({
208
+ year: headerInfo.year(),
209
+ month: date.month - 1,
210
+ date: date.day,
211
+ });
212
+ if (dateAsMoment.isSameOrAfter(today, 'date')) {
213
+ holidays.push(date);
214
+ }
215
+ },
216
+ );
217
+ }
218
+
219
+ holiday.lunar.forEach((item: {lunarDay: any; lunarMonth: any}) => {
220
+ if (currentYear instanceof Array) {
221
+ const solar1 = converter.LunarToSolar({
222
+ lunarDay: item.lunarDay,
223
+ lunarMonth: item.lunarMonth,
224
+ lunarYear: currentYear[0],
225
+ });
226
+ const solar2 = converter.LunarToSolar({
227
+ lunarDay: item.lunarDay,
228
+ lunarMonth: item.lunarMonth,
229
+ lunarYear: currentYear[1],
230
+ });
231
+ const solar1AsMoment = moment(
232
+ formatYYYYMMDD(
233
+ solar1.solarDay,
234
+ solar1.solarMonth,
235
+ solar1.solarYear,
236
+ ),
237
+ );
238
+ const solar2AsMoment = moment(
239
+ formatYYYYMMDD(
240
+ solar2.solarDay,
241
+ solar2.solarMonth,
242
+ solar2.solarYear,
243
+ ),
244
+ );
245
+ if (
246
+ solar1AsMoment.isBetween(startDate, endDate) &&
247
+ solar1AsMoment.isSameOrAfter(today, 'date')
248
+ ) {
249
+ holidays.push({
250
+ ...item,
251
+ day: solar1.solarDay,
252
+ month: solar1.solarMonth,
253
+ });
254
+ } else if (
255
+ solar2AsMoment.isBetween(startDate, endDate) &&
256
+ solar2AsMoment.isSameOrAfter(today, 'date')
257
+ ) {
258
+ holidays.push({
259
+ ...item,
260
+ day: solar2.solarDay,
261
+ month: solar2.solarMonth,
262
+ });
263
+ }
264
+ } else {
265
+ const solar = converter.LunarToSolar({
266
+ lunarDay: item.lunarDay,
267
+ lunarMonth: item.lunarMonth,
268
+ lunarYear: currentYear,
269
+ });
270
+ const solarAsMoment = moment(
271
+ formatYYYYMMDD(solar.solarDay, solar.solarMonth, solar.solarYear),
272
+ );
273
+ if (
274
+ solarAsMoment.isBetween(startDate, endDate) &&
275
+ solarAsMoment.isSameOrAfter(today, 'date')
276
+ ) {
277
+ holidays.push({
278
+ ...item,
279
+ day: solar.solarDay,
280
+ month: solar.solarMonth,
281
+ });
282
+ }
283
+ }
284
+ });
285
+
286
+ return sortByDate(holidays);
287
+ }
288
+ return [];
289
+ },
290
+ I18N_MAP,
291
+ };
292
+
293
+ export default Utils;