@momo-kits/calendar 0.168.1-beta.18 → 0.168.1-beta.19

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/CalendarPro.tsx CHANGED
@@ -7,16 +7,18 @@ import React, {
7
7
  forwardRef,
8
8
  useImperativeHandle,
9
9
  } from 'react';
10
- import { ScrollView, View } from 'react-native';
10
+ import { Platform, ScrollView, View } from 'react-native';
11
11
  import moment, { Moment } from 'moment';
12
12
  import {
13
13
  ApplicationContext,
14
14
  CheckBox,
15
+ Colors,
15
16
  Spacing,
16
17
  Text,
17
18
  } from '@momo-kits/foundation';
18
19
  import MonthList from './MonthList';
19
20
  import HeaderControl from './HeaderControl';
21
+ import LunarDateConverter from './LunarDateConverter';
20
22
  import Util from './Util';
21
23
  import {
22
24
  CalendarProProps,
@@ -25,7 +27,7 @@ import {
25
27
  MonthListRef,
26
28
  Holidays,
27
29
  } from './types';
28
- import { ContainerContext } from './ContainerContext';
30
+ import { ContainerContext } from './index';
29
31
  import styles from './styles';
30
32
 
31
33
  const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
@@ -51,7 +53,8 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
51
53
  const { theme, translate } = useContext(ApplicationContext);
52
54
  const size = useContext(ContainerContext);
53
55
 
54
- const headerRef = useRef<Moment>(moment());
56
+ const today = useMemo(() => moment(), []);
57
+ const headerRef = useRef<Moment>(today.clone());
55
58
  const headerControlRef = useRef<HeaderControlRef>(null);
56
59
  const monthListRef = useRef<MonthListRef>(null);
57
60
 
@@ -81,6 +84,10 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
81
84
  const [temp, setTemp] = useState<any[]>([]);
82
85
  const [headerKey, setHeaderKey] = useState<string>('');
83
86
  const [ownUpdate, setOwnUpdate] = useState<boolean>(false);
87
+ const converter: any = useMemo(
88
+ () => (new LunarDateConverter()),
89
+ [],
90
+ );
84
91
 
85
92
  useImperativeHandle(ref, () => ({
86
93
  setDoubleDateAndTabIndex: (
@@ -92,6 +99,11 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
92
99
  if (firstDate) setStartDate(firstDate);
93
100
  if (secondDate !== undefined) setEndDate(secondDate);
94
101
  },
102
+ setDateRange: (dateRange: { startDate: any; endDate: any }) => {
103
+ if (monthListRef.current && dateRange.startDate) {
104
+ monthListRef.current.scrollToMonth(moment(dateRange.startDate));
105
+ }
106
+ },
95
107
  }));
96
108
 
97
109
  // Sync showLunar prop
@@ -193,12 +205,7 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
193
205
  }
194
206
 
195
207
  return (
196
- <View
197
- style={[
198
- styles.container,
199
- { backgroundColor: theme.colors.background.surface },
200
- ]}
201
- >
208
+ <View style={styles.container}>
202
209
  <HeaderControl
203
210
  ref={headerControlRef}
204
211
  selectedDate={propSelectedDate}
@@ -211,15 +218,11 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
211
218
  {[1, 2, 3, 4, 5, 6, 7].map(dayIdx => (
212
219
  <Text
213
220
  key={dayIdx}
214
- color={
215
- dayIdx >= 6
216
- ? theme.colors.error.primary
217
- : theme.colors.text.default
218
- }
221
+ color={dayIdx >= 6 ? Colors.red_03 : Colors.black_17}
219
222
  typography="label_s_medium"
220
223
  style={[
221
224
  styles.textDay,
222
- { width: (size.width - Spacing.S * 2) / 7 },
225
+ { width: (size.width - Spacing.M * 2) / 7 },
223
226
  ]}
224
227
  >
225
228
  {translate?.(Util.mapWeeKDate(dayIdx))}
@@ -228,6 +231,7 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
228
231
  </View>
229
232
  <MonthList
230
233
  ref={monthListRef}
234
+ today={today}
231
235
  minDate={minDate}
232
236
  maxDate={maxDate}
233
237
  startDate={startDate!}
@@ -237,6 +241,7 @@ const CalendarPro = forwardRef<CalendarProRef, CalendarProProps>(
237
241
  isShowLunar={!isOffLunar && showLunar}
238
242
  isDoubleDateMode={isDoubleDateMode}
239
243
  tabSelected={tabSelected}
244
+ lunarConverter={converter}
240
245
  holidays={holidays}
241
246
  selectedDate={propSelectedDate}
242
247
  priceList={priceListFormat}
package/Day.tsx CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  exportFontFamily,
10
10
  } from '@momo-kits/foundation';
11
11
  import { DayProps } from './types';
12
- import { ContainerContext } from './ContainerContext';
12
+ import { ContainerContext } from './index';
13
13
  import styles from './styles';
14
14
 
15
15
  const Day: React.FC<DayProps> = props => {
@@ -36,12 +36,13 @@ const Day: React.FC<DayProps> = props => {
36
36
 
37
37
  const { theme } = useContext(ApplicationContext);
38
38
  const size = useContext(ContainerContext);
39
+ const scaledHeight = useScaleSize(48);
39
40
  const scaledSpacing = useScaleSize(Spacing.XS);
40
41
  const scaledFontSize = useScaleSize(8);
41
42
  const scaledLineHeight = useScaleSize(12);
42
43
  const lunarFontFamily = exportFontFamily('regular');
43
44
 
44
- const itemWidth = (size.width - Spacing.S * 2) / 7;
45
+ const itemWidth = (size.width - Spacing.M) / 7;
45
46
  if (isEmpty || !date) {
46
47
  return <View style={[styles.dayContainer, { width: itemWidth }]} />;
47
48
  }
@@ -113,6 +114,7 @@ const Day: React.FC<DayProps> = props => {
113
114
  paddingVertical: !!price ? Spacing.XS : Spacing.S,
114
115
  },
115
116
  (isStart || isEnd) && { backgroundColor: theme.colors.primary },
117
+ !!price && { height: scaledHeight },
116
118
  { height: size.height },
117
119
  ]}
118
120
  disabled={!(isValid && isInScope)}
package/HeaderControl.tsx CHANGED
@@ -7,6 +7,7 @@ import React, {
7
7
  } from 'react';
8
8
  import {TouchableOpacity, View} from 'react-native';
9
9
  import moment from 'moment';
10
+ import Moment from 'moment';
10
11
  import {ApplicationContext, Icon, Text} from '@momo-kits/foundation';
11
12
 
12
13
  import Util from './Util';
@@ -18,7 +19,7 @@ const HeaderControl: ForwardRefRenderFunction<
18
19
  HeaderControlProps
19
20
  > = ({onPressBackArrow, onPressNextArrow, selectedDate}, ref) => {
20
21
  const {translate} = useContext(ApplicationContext);
21
- const [info, setInfo] = useState<{date: moment.Moment}>({
22
+ const [info, setInfo] = useState<{date: Moment.Moment}>({
22
23
  date: moment(selectedDate || new Date()),
23
24
  });
24
25
 
@@ -0,0 +1,206 @@
1
+ const removeFractionalPart = (number: string | number) =>
2
+ parseInt(String(number));
3
+
4
+ /* Compute the (integral) Julian day number of day dd/mm/yyyy, i.e., the number
5
+ * of days between 1/1/4713 BC (Julian calendar) and dd/mm/yyyy.
6
+ * Formula from http://www.tondering.dk/claus/calendar.html
7
+ */
8
+ const jdFromDate = (dd: number, mm: number, yy: number) => {
9
+ let a, y, m, jd;
10
+ a = removeFractionalPart((14 - mm) / 12);
11
+ y = yy + 4800 - a;
12
+ m = mm + 12 * a - 3;
13
+ jd =
14
+ dd +
15
+ removeFractionalPart((153 * m + 2) / 5) +
16
+ 365 * y +
17
+ removeFractionalPart(y / 4) -
18
+ removeFractionalPart(y / 100) +
19
+ removeFractionalPart(y / 400) -
20
+ 32045;
21
+ if (jd < 2299161) {
22
+ jd =
23
+ dd +
24
+ removeFractionalPart((153 * m + 2) / 5) +
25
+ 365 * y +
26
+ removeFractionalPart(y / 4) -
27
+ 32083;
28
+ }
29
+ return jd;
30
+ };
31
+
32
+ /* Compute the time of the k-th new moon after the new moon of 1/1/1900 13:52 UCT
33
+ * (measured as the number of days since 1/1/4713 BC noon UCT, e.g., 2451545.125 is 1/1/2000 15:00 UTC).
34
+ * Returns a floating number, e.g., 2415079.9758617813 for k=2 or 2414961.935157746 for k=-2
35
+ * Algorithm from: "Astronomical Algorithms" by Jean Meeus, 1998
36
+ */
37
+ const NewMoon = (k: number) => {
38
+ let T, T2, T3, dr, Jd1, M, Mpr, F, C1, deltat, JdNew;
39
+ T = k / 1236.85; // Time in Julian centuries from 1900 January 0.5
40
+ T2 = T * T;
41
+ T3 = T2 * T;
42
+ dr = Math.PI / 180;
43
+ Jd1 = 2415020.75933 + 29.53058868 * k + 0.0001178 * T2 - 0.000000155 * T3;
44
+ Jd1 = Jd1 + 0.00033 * Math.sin((166.56 + 132.87 * T - 0.009173 * T2) * dr); // Mean new moon
45
+ M = 359.2242 + 29.10535608 * k - 0.0000333 * T2 - 0.00000347 * T3; // Sun's mean anomaly
46
+ Mpr = 306.0253 + 385.81691806 * k + 0.0107306 * T2 + 0.00001236 * T3; // Moon's mean anomaly
47
+ F = 21.2964 + 390.67050646 * k - 0.0016528 * T2 - 0.00000239 * T3; // Moon's argument of latitude
48
+ C1 =
49
+ (0.1734 - 0.000393 * T) * Math.sin(M * dr) + 0.0021 * Math.sin(2 * dr * M);
50
+ C1 = C1 - 0.4068 * Math.sin(Mpr * dr) + 0.0161 * Math.sin(dr * 2 * Mpr);
51
+ C1 = C1 - 0.0004 * Math.sin(dr * 3 * Mpr);
52
+ C1 = C1 + 0.0104 * Math.sin(dr * 2 * F) - 0.0051 * Math.sin(dr * (M + Mpr));
53
+ C1 =
54
+ C1 -
55
+ 0.0074 * Math.sin(dr * (M - Mpr)) +
56
+ 0.0004 * Math.sin(dr * (2 * F + M));
57
+ C1 =
58
+ C1 -
59
+ 0.0004 * Math.sin(dr * (2 * F - M)) -
60
+ 0.0006 * Math.sin(dr * (2 * F + Mpr));
61
+ C1 =
62
+ C1 +
63
+ 0.001 * Math.sin(dr * (2 * F - Mpr)) +
64
+ 0.0005 * Math.sin(dr * (2 * Mpr + M));
65
+ if (T < -11) {
66
+ deltat =
67
+ 0.001 +
68
+ 0.000839 * T +
69
+ 0.0002261 * T2 -
70
+ 0.00000845 * T3 -
71
+ 0.000000081 * T * T3;
72
+ } else {
73
+ deltat = -0.000278 + 0.000265 * T + 0.000262 * T2;
74
+ }
75
+ JdNew = Jd1 + C1 - deltat;
76
+ return JdNew;
77
+ };
78
+
79
+ /* Compute the longitude of the sun at any time.
80
+ * Parameter: floating number jdn, the number of days since 1/1/4713 BC noon
81
+ * Algorithm from: "Astronomical Algorithms" by Jean Meeus, 1998
82
+ */
83
+ const SunLongitude = (jdn: number) => {
84
+ let T, T2, dr, M, L0, DL, L;
85
+ T = (jdn - 2451545.0) / 36525; // Time in Julian centuries from 2000-01-01 12:00:00 GMT
86
+ T2 = T * T;
87
+ dr = Math.PI / 180; // degree to radian
88
+ M = 357.5291 + 35999.0503 * T - 0.0001559 * T2 - 0.00000048 * T * T2; // mean anomaly, degree
89
+ L0 = 280.46645 + 36000.76983 * T + 0.0003032 * T2; // mean longitude, degree
90
+ DL = (1.9146 - 0.004817 * T - 0.000014 * T2) * Math.sin(dr * M);
91
+ DL =
92
+ DL +
93
+ (0.019993 - 0.000101 * T) * Math.sin(dr * 2 * M) +
94
+ 0.00029 * Math.sin(dr * 3 * M);
95
+ L = L0 + DL; // true longitude, degree
96
+ L = L * dr;
97
+ L = L - Math.PI * 2 * Math.sin(L / (Math.PI * 2)); // Normalize to (0, 2*PI)
98
+ return L;
99
+ };
100
+
101
+ /* Compute sun position at midnight of the day with the given Julian day number.
102
+ * The time zone if the time difference between local time and UTC: 7.0 for UTC+7:00.
103
+ * The returns a number between 0 and 11.
104
+ * From the day after March equinox and the 1st major term after March equinox, 0 is returned.
105
+ * After that, return 1, 2, 3 ...
106
+ */
107
+ const getSunLongitude = (dayNumber: number, timeZone: number) => {
108
+ return removeFractionalPart(
109
+ (SunLongitude(dayNumber - 0.5 - timeZone / 24) / Math.PI) * 6
110
+ );
111
+ };
112
+
113
+ /* Compute the day of the k-th new moon in the given time zone.
114
+ * The time zone if the time difference between local time and UTC: 7.0 for UTC+7:00
115
+ */
116
+ const getNewMoonDay = (k: number, timeZone: number) => {
117
+ return removeFractionalPart(NewMoon(k) + 0.5 + timeZone / 24);
118
+ };
119
+
120
+ /* Find the day that starts the luner month 11 of the given year for the given time zone */
121
+ const getLunarMonth11 = (yy: number, timeZone: number) => {
122
+ let k, off, nm, sunLong;
123
+ //off = jdFromDate(31, 12, yy) - 2415021.076998695;
124
+ off = jdFromDate(31, 12, yy) - 2415021;
125
+ k = removeFractionalPart(off / 29.530588853);
126
+ nm = getNewMoonDay(k, timeZone);
127
+ sunLong = getSunLongitude(nm, timeZone); // sun longitude at local midnight
128
+ if (sunLong >= 9) {
129
+ nm = getNewMoonDay(k - 1, timeZone);
130
+ }
131
+ return nm;
132
+ };
133
+
134
+ /* Find the index of the leap month after the month starting on the day a11. */
135
+ const getLeapMonthOffset = (a11: number, timeZone: number) => {
136
+ let k, last, arc, i;
137
+ k = removeFractionalPart((a11 - 2415021.076998695) / 29.530588853 + 0.5);
138
+ last = 0;
139
+ i = 1; // We start with the month following lunar month 11
140
+ arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);
141
+ do {
142
+ last = arc;
143
+ i++;
144
+ arc = getSunLongitude(getNewMoonDay(k + i, timeZone), timeZone);
145
+ } while (arc !== last && i < 14);
146
+ return i - 1;
147
+ };
148
+
149
+ /* Comvert solar date dd/mm/yyyy to the corresponding lunar date */
150
+ export const convertSolar2Lunar = (
151
+ dd: number,
152
+ mm: number,
153
+ yy: number,
154
+ timeZone: number
155
+ ) => {
156
+ let k,
157
+ dayNumber,
158
+ monthStart,
159
+ a11,
160
+ b11,
161
+ lunarDay,
162
+ lunarMonth,
163
+ lunarYear,
164
+ lunarLeap;
165
+ dayNumber = jdFromDate(dd, mm, yy);
166
+ k = removeFractionalPart((dayNumber - 2415021.076998695) / 29.530588853);
167
+ monthStart = getNewMoonDay(k + 1, timeZone);
168
+ if (monthStart > dayNumber) {
169
+ monthStart = getNewMoonDay(k, timeZone);
170
+ }
171
+ //alert(dayNumber+" -> "+monthStart);
172
+ a11 = getLunarMonth11(yy, timeZone);
173
+ b11 = a11;
174
+ if (a11 >= monthStart) {
175
+ lunarYear = yy;
176
+ a11 = getLunarMonth11(yy - 1, timeZone);
177
+ } else {
178
+ lunarYear = yy + 1;
179
+ b11 = getLunarMonth11(yy + 1, timeZone);
180
+ }
181
+ lunarDay = dayNumber - monthStart + 1;
182
+ let diff = removeFractionalPart((monthStart - a11) / 29);
183
+ lunarLeap = 0;
184
+ lunarMonth = diff + 10;
185
+ if (b11 - a11 > 365) {
186
+ let leapMonthDiff = getLeapMonthOffset(a11, timeZone);
187
+ if (diff >= leapMonthDiff) {
188
+ lunarMonth = diff + 10;
189
+ if (diff === leapMonthDiff) {
190
+ lunarLeap = 1;
191
+ }
192
+ }
193
+ }
194
+ if (lunarMonth > 12) {
195
+ lunarMonth = lunarMonth - 12;
196
+ }
197
+ if (lunarMonth >= 11 && diff < 4) {
198
+ lunarYear -= 1;
199
+ }
200
+ return {
201
+ lunarDay,
202
+ lunarMonth: lunarMonth,
203
+ lunarYear,
204
+ isleap: lunarLeap,
205
+ };
206
+ };
package/Month.tsx CHANGED
@@ -4,7 +4,7 @@ import moment, {Moment} from 'moment';
4
4
  import Day from './Day';
5
5
  import {useScaleSize, Spacing} from '@momo-kits/foundation';
6
6
  import {MonthProps} from './types';
7
- import {ContainerContext} from './ContainerContext';
7
+ import {ContainerContext} from './index';
8
8
 
9
9
  const Month: React.FC<MonthProps> = ({
10
10
  dateList,
package/MonthList.tsx CHANGED
@@ -5,6 +5,7 @@ import React, {
5
5
  useImperativeHandle,
6
6
  useCallback,
7
7
  useContext,
8
+ useEffect,
8
9
  useLayoutEffect,
9
10
  useMemo,
10
11
  useRef,
@@ -16,7 +17,7 @@ import {
16
17
  NativeSyntheticEvent,
17
18
  Platform,
18
19
  } from 'react-native';
19
- import { ContainerContext } from './ContainerContext';
20
+ import { ContainerContext } from './index';
20
21
  import LunarDateConverter from './LunarDateConverter';
21
22
  import Month from './Month';
22
23
  import { MonthListProps, MonthListRef } from './types';
@@ -38,22 +39,15 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
38
39
  isShowLunar,
39
40
  isDoubleDateMode,
40
41
  disabledDays,
41
- tabSelected,
42
- onChoose,
43
- bestPriceColor,
44
42
  onScrollCalendar,
43
+ ...restProps
45
44
  } = props;
46
45
 
47
46
  const size = useContext(ContainerContext);
48
47
  const scaledHeight48 = useScaleSize(48);
49
48
  const listRef = useRef<FlatList>(null);
50
49
  const currentKey = useRef<string | null>(null);
51
- const targetIndex = useRef<number | null>(null);
52
50
  const [heightStyle, setHeightStyle] = useState({});
53
- const onScrollCalendarRef = useRef(onScrollCalendar);
54
- onScrollCalendarRef.current = onScrollCalendar;
55
- const scaledHeight48Ref = useRef(scaledHeight48);
56
- scaledHeight48Ref.current = scaledHeight48;
57
51
 
58
52
  const data = useMemo(() => {
59
53
  const months: { date: Moment; dateList: any[] }[] = [];
@@ -75,6 +69,9 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
75
69
  }
76
70
  while (cursor.month() === monthIdx) {
77
71
  const cloned = cursor.clone();
72
+ const hol = holidays.find(
73
+ h => h.day === cloned.date() && h.month === cloned.month() + 1,
74
+ );
78
75
  dayList.push({
79
76
  isEmpty: false,
80
77
  date: cloned,
@@ -83,6 +80,8 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
83
80
  solarMonth: cloned.month() + 1,
84
81
  solarYear: cloned.year(),
85
82
  }),
83
+ isSolarHoliday: !!(hol && !hol.lunar),
84
+ isLunarHoliday: !!(hol && hol.lunar),
86
85
  });
87
86
  cursor.add(1, 'days');
88
87
  }
@@ -97,7 +96,7 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
97
96
  mMin.add(1, 'month');
98
97
  }
99
98
  return months;
100
- }, [minDate, maxDate]);
99
+ }, [minDate, maxDate, holidays]);
101
100
 
102
101
  const getIndexOfMonth = useCallback(
103
102
  (date: Moment) => data.findIndex(item => item.date.isSame(date, 'month')),
@@ -139,9 +138,6 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
139
138
  (month: Moment) => {
140
139
  const idx = getIndexOfMonth(month);
141
140
  if (listRef.current && idx !== -1) {
142
- const offset = idx * pageWidth.current;
143
- targetIndex.current =
144
- Math.abs(offset - scrollOffset.current) < 1 ? null : idx;
145
141
  listRef.current.scrollToIndex({ index: idx, animated: true });
146
142
  }
147
143
  },
@@ -150,31 +146,29 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
150
146
 
151
147
  useImperativeHandle(ref, () => ({ scrollToMonth }));
152
148
 
149
+ useEffect(() => {
150
+ const timer = setTimeout(() => {
151
+ scrollToMonth(selectedDate);
152
+ }, 500);
153
+ return () => clearTimeout(timer);
154
+ }, []);
155
+
153
156
  const onViewableItemsChanged = useRef(({ changed }: { changed: any[] }) => {
154
- const visible = changed.find(token => token.isViewable);
155
- if (targetIndex.current !== null && visible?.index !== targetIndex.current) {
156
- return;
157
- }
158
- targetIndex.current = null;
159
- if (visible) {
160
- const { item, key, index } = visible;
161
- if (onScrollCalendarRef.current && item && currentKey.current !== key) {
157
+ if (changed.length > 0) {
158
+ const { item, key, index } = changed[0];
159
+ if (onScrollCalendar && item && currentKey.current !== key) {
162
160
  currentKey.current = key;
163
161
  try {
164
162
  setHeightStyle({
165
163
  height:
166
- (scaledHeight48Ref.current * item.dateList.length) / 7 +
164
+ (scaledHeight48 * item.dateList.length) / 7 +
167
165
  Spacing.L * 2 +
168
166
  Spacing.XS,
169
167
  });
170
168
  } catch {
171
169
  setHeightStyle({});
172
170
  }
173
- onScrollCalendarRef.current({
174
- date: item.date,
175
- key,
176
- currentIndex: index,
177
- });
171
+ onScrollCalendar({ date: item.date, key, currentIndex: index });
178
172
  }
179
173
  }
180
174
  }).current;
@@ -207,6 +201,7 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
207
201
  const priceListDate = priceList?.[keyMonth]?.day;
208
202
  return (
209
203
  <Month
204
+ {...restProps}
210
205
  key={index}
211
206
  month={item.date}
212
207
  dateList={item.dateList}
@@ -219,13 +214,11 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
219
214
  isShowLunar={isShowLunar}
220
215
  isDoubleDateMode={isDoubleDateMode}
221
216
  disabledDays={disabledDays}
222
- tabSelected={tabSelected}
223
- onChoose={onChoose}
224
- bestPriceColor={bestPriceColor}
225
217
  />
226
218
  );
227
219
  },
228
220
  [
221
+ restProps,
229
222
  priceList,
230
223
  holidays,
231
224
  minDate,
@@ -235,9 +228,6 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
235
228
  isShowLunar,
236
229
  isDoubleDateMode,
237
230
  disabledDays,
238
- tabSelected,
239
- onChoose,
240
- bestPriceColor,
241
231
  ],
242
232
  );
243
233
 
@@ -254,9 +244,6 @@ const MonthList = forwardRef<MonthListRef, MonthListProps>((props, ref) => {
254
244
  keyExtractor={keyExtractor}
255
245
  onScrollToIndexFailed={() => {}}
256
246
  onScroll={onScroll}
257
- onScrollBeginDrag={() => {
258
- targetIndex.current = null;
259
- }}
260
247
  scrollEnabled
261
248
  initialNumToRender={3}
262
249
  maxToRenderPerBatch={MAX_RENDER_PER_BATCH}
package/TabHeader.tsx CHANGED
@@ -1,73 +1,88 @@
1
- import React, {
2
- forwardRef,
3
- useContext,
4
- useImperativeHandle,
5
- useState,
6
- } from 'react';
7
- import { TouchableWithoutFeedback, View } from 'react-native';
1
+ import React from 'react';
2
+ import {TouchableWithoutFeedback, View} from 'react-native';
8
3
  import Moment from 'moment';
9
4
  import Util from './Util';
10
5
  import {
11
6
  ApplicationContext,
7
+ Colors,
12
8
  Radius,
13
9
  Spacing,
14
10
  Text,
15
11
  } from '@momo-kits/foundation';
16
- import { TabHeaderProps, TabHeaderRef } from './types';
12
+ import {TabHeaderProps, TabHeaderState} from './types';
17
13
 
18
- const TabHeader = forwardRef<TabHeaderRef, TabHeaderProps>((props, ref) => {
19
- const { label, disabled, id, onChangeTab } = props;
20
- const { theme, translate } = useContext(ApplicationContext);
21
- const [active, setActive] = useState<boolean | undefined>(props.activeTab);
22
- const [date, setDate] = useState<Moment.Moment | undefined>(
23
- props.date ? Moment(props.date) : undefined,
24
- );
14
+ export default class TabHeader extends React.Component<
15
+ TabHeaderProps,
16
+ TabHeaderState
17
+ > {
18
+ static contextType = ApplicationContext;
19
+ context: React.ContextType<typeof ApplicationContext> = {} as React.ContextType<typeof ApplicationContext>;
20
+ label;
21
+ defaultDate: Moment.Moment | null;
25
22
 
26
- useImperativeHandle(ref, () => ({
27
- updateView: (nextDate, activeTab) => {
28
- setDate(nextDate ? Moment(nextDate) : undefined);
29
- setActive(activeTab);
30
- },
31
- setActiveTab: setActive,
32
- }));
23
+ constructor(props: TabHeaderProps) {
24
+ super(props);
25
+ this.state = {
26
+ active: props.activeTab,
27
+ };
28
+ this.label = props.label;
29
+ this.defaultDate = props.date ? Moment(props.date) : null;
30
+ }
33
31
 
34
- const weekday = (day: Moment.Moment) =>
35
- translate?.(Util.WEEKDAYSFROMMOMENT[day.day()]);
36
- const isHighlighted = active && !disabled;
32
+ onChangeTab = () => {
33
+ const {onChangeTab, id} = this.props;
34
+ onChangeTab?.(id);
35
+ };
37
36
 
38
- const formattedDate = date ? date.format('DD/MM/YYYY') : '';
39
- const dayOfWeek = date ? `${weekday(date)}, ` : '';
37
+ updateView = (date?: Moment.Moment | Date | null, activeTab?: boolean) => {
38
+ this.setState({
39
+ date: date ? Moment(date) : undefined,
40
+ active: activeTab,
41
+ });
42
+ };
40
43
 
41
- return (
42
- <TouchableWithoutFeedback
43
- disabled={disabled}
44
- onPress={() => onChangeTab?.(id)}
45
- >
46
- <View
47
- style={[
48
- {
49
- flex: 1,
50
- padding: Spacing.S,
51
- borderRadius: Radius.S,
52
- },
53
- isHighlighted && {
54
- backgroundColor: theme.colors.background.surface,
55
- },
56
- ]}
57
- >
58
- <Text
59
- typography={'description_default_regular'}
60
- color={theme.colors.text.hint}
61
- >
62
- {translate?.(label)}
63
- </Text>
64
- <Text typography={'header_s_semibold'}>
65
- {`${dayOfWeek || '--'} `}
66
- {formattedDate || '--/--/----'}
67
- </Text>
68
- </View>
69
- </TouchableWithoutFeedback>
70
- );
71
- });
44
+ setActiveTab = (active: boolean) => {
45
+ this.setState({active});
46
+ };
72
47
 
73
- export default TabHeader;
48
+ render() {
49
+ const {label, disabled} = this.props;
50
+ const {date, active} = this.state;
51
+ const {theme, translate} = this.context;
52
+ const defaultDate = this.defaultDate;
53
+ const weekday = (day: Moment.Moment) =>
54
+ translate?.(Util.WEEKDAYSFROMMOMENT[day.day()]);
55
+ const isHighlighted = active && !disabled;
56
+
57
+ const formattedDateFromState = date ? date.format('DD/MM/YYYY') : '';
58
+ const formattedDateFromDefault = defaultDate?.format('DD/MM/YYYY') ?? '';
59
+ const dayOfWeekFromState = date ? `${weekday(date)}, ` : '';
60
+ const dayOfWeekFromDefault = defaultDate ? `${weekday(defaultDate)},` : '';
61
+
62
+ return (
63
+ <TouchableWithoutFeedback disabled={disabled} onPress={this.onChangeTab}>
64
+ <View
65
+ style={[
66
+ {
67
+ flex: 1,
68
+ padding: Spacing.S,
69
+ borderRadius: Radius.S,
70
+ },
71
+ isHighlighted && {
72
+ backgroundColor: theme.colors.background.surface,
73
+ },
74
+ ]}>
75
+ <Text
76
+ typography={'description_default_regular'}
77
+ color={Colors.black_12}>
78
+ {translate?.(label)}
79
+ </Text>
80
+ <Text typography={'header_s_semibold'}>
81
+ {`${dayOfWeekFromState || dayOfWeekFromDefault || '--'} `}
82
+ {formattedDateFromState || formattedDateFromDefault || '--/--/----'}
83
+ </Text>
84
+ </View>
85
+ </TouchableWithoutFeedback>
86
+ );
87
+ }
88
+ }
package/Util.ts CHANGED
@@ -3,8 +3,50 @@ import LunarDateConverter from './LunarDateConverter';
3
3
  import holiday from './holidayData';
4
4
  import {Holidays} from './types';
5
5
 
6
- // @ts-ignore
7
- const converter = new LunarDateConverter();
6
+ const I18N_MAP = {
7
+ en: {
8
+ w: ['', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat', 'Sun'],
9
+ weekday: [
10
+ '',
11
+ 'Monday',
12
+ 'Tuesday',
13
+ 'Wednesday',
14
+ 'Thursday',
15
+ 'Friday',
16
+ 'Saturday',
17
+ 'Sunday',
18
+ ],
19
+ text: {
20
+ start: 'Start',
21
+ end: 'End',
22
+ date: 'Date',
23
+ save: 'Save',
24
+ clear: 'Reset',
25
+ },
26
+ date: 'DD / MM',
27
+ },
28
+ vi: {
29
+ w: ['', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
30
+ weekday: [
31
+ '',
32
+ 'Thứ hai',
33
+ 'Thứ ba',
34
+ 'Thứ tư',
35
+ 'Thứ năm',
36
+ 'Thứ sáu',
37
+ 'Thứ bảy',
38
+ 'Chủ nhật',
39
+ ],
40
+ text: {
41
+ start: 'Start',
42
+ end: 'End',
43
+ date: 'Date',
44
+ save: 'Save',
45
+ clear: 'Reset',
46
+ },
47
+ date: 'DD / MM',
48
+ },
49
+ };
8
50
 
9
51
  const Solar = (momentDate: moment.Moment) => ({
10
52
  solarDay: momentDate.date(),
@@ -64,8 +106,39 @@ const sortByDate = (arr: Holidays[]): Holidays[] => {
64
106
  };
65
107
 
66
108
  const Utils = {
109
+ WEEKDAYS: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
110
+
67
111
  WEEKDAYSFROMMOMENT: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
68
112
 
113
+ WEEKDAYSNAME: [
114
+ 'Thứ 2',
115
+ 'Thứ 3',
116
+ 'Thứ 4',
117
+ 'Thứ 5',
118
+ 'Thứ 6',
119
+ 'Thứ 7',
120
+ 'Chủ nhật',
121
+ ],
122
+
123
+ MONTHS: [
124
+ 'Tháng 1',
125
+ 'Tháng 2',
126
+ 'Tháng 3',
127
+ 'Tháng 4',
128
+ 'Tháng 5',
129
+ 'Tháng 6',
130
+ 'Tháng 7',
131
+ 'Tháng 8',
132
+ 'Tháng 9',
133
+ 'Tháng 10',
134
+ 'Tháng 11',
135
+ 'Tháng 12',
136
+ ],
137
+
138
+ MAX_ROWS: 7,
139
+
140
+ MAX_COLUMNS: 7,
141
+
69
142
  mapWeeKDate(i: number) {
70
143
  const date = ['', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
71
144
  return date[i];
@@ -89,11 +162,36 @@ const Utils = {
89
162
  return month.get(i);
90
163
  },
91
164
 
165
+ mapMonthShorten(i: number) {
166
+ const month = new Map([
167
+ [1, 'Jan'],
168
+ [2, 'Feb'],
169
+ [3, 'Mar'],
170
+ [4, 'Apr'],
171
+ [5, 'May'],
172
+ [6, 'Jun'],
173
+ [7, 'Jul'],
174
+ [8, 'Aug'],
175
+ [9, 'Sep'],
176
+ [10, 'Oct'],
177
+ [11, 'Nov'],
178
+ [12, 'Dec'],
179
+ ]);
180
+ return month.get(i);
181
+ },
182
+
183
+ getDaysInMonth(month: number, year: number) {
184
+ const lastDayOfMonth = new Date(year, month + 1, 0);
185
+ return lastDayOfMonth.getDate();
186
+ },
187
+
92
188
  /**
93
189
  * Get sorted list of holidays within the given month.
94
190
  */
95
191
  getHolidaysInMonth(headerInfo: moment.Moment): Holidays[] {
96
192
  if (headerInfo) {
193
+ // @ts-ignore
194
+ const converter = new LunarDateConverter();
97
195
  const startDate = moment(headerInfo).startOf('month');
98
196
  const endDate = moment(headerInfo).endOf('month');
99
197
  const minLunarDate = converter.SolarToLunar(Solar(startDate));
@@ -181,6 +279,7 @@ const Utils = {
181
279
  }
182
280
  return [];
183
281
  },
282
+ I18N_MAP,
184
283
  };
185
284
 
186
285
  export default Utils;
package/index.tsx CHANGED
@@ -1,5 +1,11 @@
1
1
  /* eslint-disable react-hooks/exhaustive-deps */
2
- import React, { useState, useRef, useEffect, useContext } from 'react';
2
+ import React, {
3
+ useState,
4
+ useRef,
5
+ useEffect,
6
+ useContext,
7
+ createContext,
8
+ } from 'react';
3
9
  import { Dimensions, LayoutChangeEvent, View } from 'react-native';
4
10
  import moment from 'moment';
5
11
  import {
@@ -12,12 +18,13 @@ import {
12
18
  } from '@momo-kits/foundation';
13
19
  import CalendarPro from './CalendarPro';
14
20
  import TabHeader from './TabHeader';
15
- import { CalendarProps, CalendarProRef, TabHeaderRef } from './types';
16
- import { ContainerContext } from './ContainerContext';
21
+ import { CalendarProps, CalendarProRef } from './types';
17
22
  import styles from './styles';
18
23
 
19
24
  const DOUBLE = 'doubleDate';
20
25
 
26
+ export const ContainerContext = createContext({ width: 0, height: 0 });
27
+
21
28
  const Calendar: React.FC<CalendarProps> = props => {
22
29
  const { translate, theme } = useContext(ApplicationContext);
23
30
  const scaledHeight48 = useScaleSize(48);
@@ -42,6 +49,9 @@ const Calendar: React.FC<CalendarProps> = props => {
42
49
  isHideHoliday,
43
50
  isShowLunar,
44
51
  priceList,
52
+ labelFrom,
53
+ labelTo,
54
+ isHideLabel,
45
55
  minDate = minDateDefault,
46
56
  maxDate = maxDateDefault,
47
57
  style,
@@ -75,9 +85,9 @@ const Calendar: React.FC<CalendarProps> = props => {
75
85
 
76
86
  const calendarPicker = useRef<CalendarProRef>(null);
77
87
  // hold refs to TabHeader instances
78
- const cellHeader1 = useRef<TabHeaderRef>(null);
79
- const cellHeader2 = useRef<TabHeaderRef>(null);
80
- const cellHeaderSingle = useRef<TabHeaderRef>(null);
88
+ const cellHeader1 = useRef<TabHeader>(null);
89
+ const cellHeader2 = useRef<TabHeader>(null);
90
+ const cellHeaderSingle = useRef<TabHeader>(null);
81
91
 
82
92
  // Initialize view on mount
83
93
  useEffect(() => {
@@ -207,11 +217,18 @@ const Calendar: React.FC<CalendarProps> = props => {
207
217
  const updateHeaderView = () => {
208
218
  const { onDateChange, onCTAStateChange } = props;
209
219
  if (isDoubleDateMode) {
220
+ cellHeader1.current?.updateView(selectedDate, true);
221
+ cellHeader2.current?.updateView(null, false);
222
+ calendarPicker.current?.setDoubleDateAndTabIndex(
223
+ selectedDate,
224
+ null,
225
+ tabSelected.current,
226
+ );
210
227
  doubleDate.current.first = moment(selectedDate);
211
228
  doubleDate.current.second = null;
212
229
  tabSelected.current = 1;
213
- cellHeader1.current?.updateView(selectedDate, false);
214
- cellHeader2.current?.updateView(null, true);
230
+ cellHeader2.current?.setActiveTab(true);
231
+ cellHeader1.current?.setActiveTab(false);
215
232
  calendarPicker.current?.setDoubleDateAndTabIndex(
216
233
  doubleDate.current.first,
217
234
  doubleDate.current.second,
@@ -220,17 +237,16 @@ const Calendar: React.FC<CalendarProps> = props => {
220
237
  onCTAStateChange?.(false);
221
238
  onDateChange?.({ first: doubleDate.current.first!, second: null });
222
239
  } else {
223
- const date = doubleDate.current.first ?? selectedDate;
240
+ // Reset second date when switching to single mode
224
241
  doubleDate.current.second = null;
225
- tabSelected.current = 0;
226
- calendarPicker.current?.setDoubleDateAndTabIndex(date, null, 0);
227
- onCTAStateChange?.(true);
228
- if (date.isSame(selectedDate, 'day')) {
229
- cellHeaderSingle.current?.updateView(date, true);
230
- onDateChange?.(date.toDate());
242
+ props.onCTAStateChange?.(true);
243
+ if (doubleDate.current.first) {
244
+ cellHeaderSingle.current?.updateView(doubleDate.current.first, true);
245
+ setSelectedDate(doubleDate.current.first);
231
246
  } else {
232
- setSelectedDate(date);
247
+ cellHeaderSingle.current?.updateView(selectedDate, true);
233
248
  }
249
+ props.onDateChange?.(selectedDate.toDate());
234
250
  }
235
251
  };
236
252
 
@@ -286,7 +302,7 @@ const Calendar: React.FC<CalendarProps> = props => {
286
302
  activeTab
287
303
  date={doubleDate.current.first}
288
304
  />
289
- <View style={styles.tabGap} />
305
+ <View style={{ width: 4, height: '100%' }} />
290
306
  <TabHeader
291
307
  id={1}
292
308
  ref={cellHeader2}
@@ -345,6 +361,9 @@ const Calendar: React.FC<CalendarProps> = props => {
345
361
  selectedDate={selectedDate}
346
362
  isShowLunar={isShowLunar}
347
363
  onCallbackCalendar={onCallbackCalendar}
364
+ labelFrom={labelFrom}
365
+ labelTo={labelTo}
366
+ isHideLabel={isHideLabel}
348
367
  minDate={minDate}
349
368
  maxDate={maxDate}
350
369
  isHideHoliday={isHideHoliday}
@@ -357,4 +376,4 @@ const Calendar: React.FC<CalendarProps> = props => {
357
376
  );
358
377
  };
359
378
 
360
- export { Calendar, ContainerContext };
379
+ export { Calendar };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/calendar",
3
- "version": "0.168.1-beta.18",
3
+ "version": "0.168.1-beta.19",
4
4
  "private": false,
5
5
  "main": "index.tsx",
6
6
  "peerDependencies": {
package/styles.ts CHANGED
@@ -16,10 +16,6 @@ export default StyleSheet.create({
16
16
  alignItems: 'center',
17
17
  marginBottom: Spacing.XS,
18
18
  },
19
- tabGap: {
20
- width: Spacing.XS,
21
- height: '100%',
22
- },
23
19
  headerContainer: {
24
20
  paddingHorizontal: Spacing.M,
25
21
  marginTop: Spacing.S,
@@ -61,6 +57,7 @@ export default StyleSheet.create({
61
57
  marginTop: Spacing.S,
62
58
  },
63
59
  container: {
60
+ backgroundColor: 'white',
64
61
  borderRadius: Radius.XS,
65
62
  },
66
63
  blueSeperator: {
@@ -73,6 +70,18 @@ export default StyleSheet.create({
73
70
  txtHeader: {
74
71
  textAlign: 'center',
75
72
  },
73
+ btnRight: {
74
+ width: 36,
75
+ height: 36,
76
+ justifyContent: 'center',
77
+ alignItems: 'center',
78
+ },
79
+ btnLeft: {
80
+ width: 36,
81
+ height: 36,
82
+ justifyContent: 'center',
83
+ alignItems: 'center',
84
+ },
76
85
  headerControlContainer: {
77
86
  flexDirection: 'row',
78
87
  justifyContent: 'space-between',
@@ -92,6 +101,9 @@ export default StyleSheet.create({
92
101
  alignItems: 'center',
93
102
  borderRadius: Radius.XS,
94
103
  },
104
+ dayText: {
105
+ textAlign: 'center',
106
+ },
95
107
  dayStartContainer: {
96
108
  borderTopLeftRadius: Radius.XS,
97
109
  borderBottomLeftRadius: Radius.XS,
package/types.ts CHANGED
@@ -42,6 +42,9 @@ export type CalendarProProps = {
42
42
  isDoubleDateMode: boolean;
43
43
  onDateChange: (day: Moment.Moment) => void;
44
44
  priceList?: PriceListData;
45
+ labelFrom?: string;
46
+ labelTo?: string;
47
+ isHideLabel?: boolean;
45
48
  isHideHoliday?: boolean;
46
49
  isOffLunar?: boolean;
47
50
  onCallbackCalendar?: (lunar: string, nextStateShowLunar: boolean) => void;
@@ -49,6 +52,17 @@ export type CalendarProProps = {
49
52
  bestPriceColor?: string;
50
53
  };
51
54
 
55
+ export type CalendarProState = {
56
+ startDate: Moment.Moment;
57
+ endDate: Moment.Moment;
58
+ showLunar?: boolean;
59
+ tabSelected?: number;
60
+ holidays: Holidays[];
61
+ ownUpdate?: boolean;
62
+ temp?: any;
63
+ headerKey?: string;
64
+ };
65
+
52
66
  export type DoubleDate = {
53
67
  first: Moment.Moment;
54
68
  second: Moment.Moment | null;
@@ -88,6 +102,15 @@ export type CalendarProRef = {
88
102
  secondDate?: Moment.Moment | null,
89
103
  tabSelected?: number,
90
104
  ) => void;
105
+ setDateRange: (
106
+ dateRange: { startDate: any; endDate: any },
107
+ isScrollToStartDate: any,
108
+ ) => void;
109
+ };
110
+
111
+ export type CalendarState = {
112
+ isDoubleDateMode: boolean;
113
+ containerWidth: number;
91
114
  };
92
115
 
93
116
  export type HeaderControlRef = {
@@ -111,16 +134,23 @@ export type MonthListProps = {
111
134
  endDate: Moment.Moment;
112
135
  isShowLunar: boolean;
113
136
  tabSelected?: number;
137
+ today: Moment.Moment;
114
138
  onChoose: (day: Moment.Moment) => void;
115
139
  onScrollCalendar?: (info: {
116
140
  date: Moment.Moment;
117
141
  key: string;
118
142
  currentIndex: number;
119
143
  }) => void;
144
+ lunarConverter: any;
120
145
  disabledDays?: Date[];
121
146
  bestPriceColor?: string;
122
147
  };
123
148
 
149
+ export type MonthListState = {
150
+ data?: any;
151
+ isDoubleDateMode?: boolean;
152
+ };
153
+
124
154
  export type MonthListRef = {
125
155
  scrollToMonth: (date: Moment.Moment) => void;
126
156
  };
@@ -138,8 +168,6 @@ export type MonthProps = {
138
168
  isDoubleDateMode: boolean;
139
169
  disabledDays?: Date[];
140
170
  bestPriceColor?: string;
141
- tabSelected?: number;
142
- onChoose?: (date: Moment.Moment) => void;
143
171
  };
144
172
 
145
173
  export type DayProps = {
@@ -164,9 +192,9 @@ export type DayProps = {
164
192
  bestPriceColor?: string;
165
193
  };
166
194
 
167
- export type TabHeaderRef = {
168
- updateView: (date?: Moment.Moment | Date | null, activeTab?: boolean) => void;
169
- setActiveTab: (active: boolean) => void;
195
+ export type TabHeaderState = {
196
+ active?: boolean;
197
+ date?: Moment.Moment;
170
198
  };
171
199
 
172
200
  export type TabHeaderProps = {
@@ -177,3 +205,8 @@ export type TabHeaderProps = {
177
205
  id: number;
178
206
  disabled?: boolean;
179
207
  };
208
+
209
+ export type TabHeaderRef = {
210
+ updateView: (date?: Moment.Moment | null, activeTab?: boolean) => void;
211
+ setActiveTab: (active: boolean) => void;
212
+ };
@@ -1,3 +0,0 @@
1
- import { createContext } from 'react';
2
-
3
- export const ContainerContext = createContext({ width: 0, height: 0 });