@momo-kits/calendar 0.121.0-rc.10 → 0.121.0-rc.3

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/Day.tsx CHANGED
@@ -1,4 +1,5 @@
1
- import React, {useContext} from 'react';
1
+ import React, {Component} from 'react';
2
+
2
3
  import {Text as RNText, TouchableOpacity, View} from 'react-native';
3
4
  import {
4
5
  ApplicationContext,
@@ -11,130 +12,209 @@ import {DayProps} from './types';
11
12
  import {ContainerContext} from './index';
12
13
  import styles from './styles';
13
14
 
14
- const Day: React.FC<DayProps> = props => {
15
- const {
16
- startDate,
17
- endDate,
18
- date,
19
- minDate,
20
- maxDate,
21
- isEmpty,
22
- index,
23
- isShowLunar,
24
- isDoubleDateMode,
25
- lunarDate,
26
- isSolarHoliday,
27
- isLunarHoliday,
28
- tabSelected,
29
- isDisabled = false,
30
- onChoose,
31
- price,
32
- isBestPrice,
33
- } = props;
34
-
35
- const {theme} = useContext(ApplicationContext);
36
- const size = useContext(ContainerContext);
15
+ class Day extends Component<DayProps> {
16
+ static contextType = ApplicationContext;
17
+ isFocus?: boolean;
18
+ isValid?: boolean;
19
+ isMid?: boolean;
20
+ isStart?: boolean;
21
+ isStartPart?: boolean;
22
+ isEnd?: boolean;
23
+ isWeekEnd?: boolean;
24
+ showLunar?: boolean;
25
+ lunarDate?: {lunarDay: number; lunarMonth: number};
26
+ isDoubleDateMode?: boolean;
27
+ isLunarHoliday?: boolean;
28
+ isLunarDayStart?: boolean;
29
+ isSolarHoliday?: boolean;
30
+ isInScope?: boolean;
31
+ diffPrice: any;
37
32
 
38
- const itemWidth = (size.width - Spacing.M) / 7;
39
- if (isEmpty || !date) {
40
- return <View style={[styles.dayContainer, {width: itemWidth}]} />;
33
+ constructor(props: DayProps) {
34
+ super(props);
35
+ this.statusCheck();
41
36
  }
42
37
 
43
- const isValid =
44
- !isDisabled &&
45
- (date.isAfter(minDate, 'day') || date.isSame(minDate, 'day')) &&
46
- (date.isBefore(maxDate, 'day') || date.isSame(maxDate, 'day'));
38
+ chooseDay = () => {
39
+ const {onChoose, date} = this.props;
40
+ onChoose && onChoose(date);
41
+ };
47
42
 
48
- const isMid =
49
- isDoubleDateMode &&
50
- date.isAfter(startDate, 'day') &&
51
- date.isBefore(endDate, 'day');
43
+ statusCheck = (props?: DayProps) => {
44
+ const {
45
+ startDate,
46
+ endDate,
47
+ date,
48
+ minDate,
49
+ maxDate,
50
+ empty,
51
+ index,
52
+ isShowLunar,
53
+ isDoubleDateMode,
54
+ lunarDate,
55
+ isSolarHoliday,
56
+ isLunarHoliday,
57
+ tabSelected,
58
+ isDisabled = false,
59
+ } = props || this.props;
60
+ this.isValid =
61
+ !isDisabled &&
62
+ date &&
63
+ (date >= minDate || date.isSame(minDate, 'day')) &&
64
+ (date <= maxDate || date.isSame(maxDate, 'day'));
65
+ this.isMid =
66
+ (isDoubleDateMode && date > startDate && date < endDate) ||
67
+ (!date && empty >= startDate && empty <= endDate);
68
+ this.isStart = date && date.isSame(startDate, 'd');
69
+ this.isStartPart = !!(this.isStart && endDate);
70
+ this.isEnd = isDoubleDateMode && date && date.isSame(endDate, 'day');
71
+ this.isFocus = this.isMid || this.isStart || this.isEnd;
72
+ this.isWeekEnd = index === 6 || index === 5;
73
+ this.showLunar = isShowLunar;
74
+ this.lunarDate = lunarDate;
75
+ this.isDoubleDateMode = isDoubleDateMode;
76
+ this.isLunarHoliday = isLunarHoliday;
77
+ this.isLunarDayStart = this.lunarDate && this.lunarDate.lunarDay === 1;
78
+ this.isSolarHoliday = isSolarHoliday;
79
+ this.isInScope = isDoubleDateMode
80
+ ? tabSelected === 0 ||
81
+ (tabSelected === 1 &&
82
+ startDate &&
83
+ date &&
84
+ date.isSameOrAfter(startDate, 'day'))
85
+ : true;
52
86
 
53
- const isStart = date.isSame(startDate, 'day');
54
- const isStartPart = !!(isStart && endDate);
55
- const isEnd = isDoubleDateMode && date.isSame(endDate, 'day');
56
- const isWeekEnd = index === 6 || index === 5;
57
- const showLunar = isShowLunar;
87
+ return this.isFocus || this.diffPrice;
88
+ };
58
89
 
59
- const isInScope = isDoubleDateMode
60
- ? tabSelected === 0 || date.isSameOrAfter(startDate, 'day')
61
- : true;
90
+ shouldComponentUpdate(nextProps: DayProps) {
91
+ const {
92
+ isDoubleDateMode,
93
+ isShowLunar,
94
+ tabSelected,
95
+ isSolarHoliday,
96
+ isLunarHoliday,
97
+ price,
98
+ isBestPrice,
99
+ startDate,
100
+ endDate,
101
+ } = this.props;
102
+ const prevStatus = this.isFocus;
103
+ const selectionModeChange = isDoubleDateMode !== nextProps.isDoubleDateMode;
104
+ const lunarChange = isShowLunar !== nextProps.isShowLunar;
105
+ const nextStatus = this.statusCheck(nextProps);
106
+ const tabChange = tabSelected !== nextProps.tabSelected;
107
+ const solarHoliday = isSolarHoliday !== nextProps.isSolarHoliday;
108
+ const lunarHoliday = isLunarHoliday !== nextProps.isLunarHoliday;
109
+ const diffPrice =
110
+ price !== nextProps.price && isBestPrice !== nextProps.isBestPrice;
111
+ const startDateChange =
112
+ startDate && !startDate?.isSame?.(nextProps.startDate, 'day');
113
+ const endDateChange =
114
+ endDate && !endDate?.isSame?.(nextProps.endDate, 'day');
115
+ return (
116
+ prevStatus !== nextStatus ||
117
+ selectionModeChange ||
118
+ lunarChange ||
119
+ tabChange ||
120
+ solarHoliday ||
121
+ lunarHoliday ||
122
+ diffPrice ||
123
+ startDateChange ||
124
+ endDateChange
125
+ );
126
+ }
62
127
 
63
- const chooseDay = () => {
64
- onChoose && onChoose(date);
65
- };
128
+ render() {
129
+ const {theme} = this.context;
130
+ const {date, empty, price, isBestPrice} = this.props;
131
+ const text = date ? date.date() : '';
132
+ let textColor = theme.colors.text.default;
133
+ let lunarTextColor = theme.colors.text.hint;
134
+ let priceColor = theme.colors.text.default;
66
135
 
67
- let textColor = theme.colors.text.default;
68
- let lunarTextColor = theme.colors.text.hint;
69
- let priceColor = theme.colors.text.default;
136
+ if (this.isWeekEnd || this.isSolarHoliday || this.isLunarHoliday) {
137
+ textColor = theme.colors.error.primary;
138
+ priceColor = theme.colors.error.primary;
139
+ }
70
140
 
71
- if (isWeekEnd || isSolarHoliday || isLunarHoliday) {
72
- textColor = theme.colors.error.primary;
73
- priceColor = theme.colors.error.primary;
74
- }
141
+ if (isBestPrice) {
142
+ priceColor = theme.colors.error.primary;
143
+ }
75
144
 
76
- if (isBestPrice) {
77
- priceColor = theme.colors.error.primary;
78
- }
145
+ if (this.isStart || this.isEnd) {
146
+ textColor = Colors.black_01;
147
+ priceColor = Colors.black_01;
148
+ lunarTextColor = Colors.black_01;
149
+ }
79
150
 
80
- if (isStart || isEnd) {
81
- textColor = Colors.black_01;
82
- priceColor = Colors.black_01;
83
- lunarTextColor = Colors.black_01;
84
- }
151
+ if (!this.isValid || !this.isInScope) {
152
+ textColor = theme.colors.text.disable;
153
+ priceColor = theme.colors.text.disable;
154
+ lunarTextColor = theme.colors.text.disable;
155
+ }
85
156
 
86
- if (!isValid || !isInScope) {
87
- textColor = theme.colors.text.disable;
88
- priceColor = theme.colors.text.disable;
89
- lunarTextColor = theme.colors.text.disable;
157
+ return (
158
+ <ContainerContext.Consumer>
159
+ {size => {
160
+ const itemWidth = (size.width - Spacing.M) / 7;
161
+ return (
162
+ <View style={[styles.dayContainer, {width: itemWidth}]}>
163
+ <View
164
+ style={[
165
+ this.isMid &&
166
+ !empty && {
167
+ backgroundColor: theme.colors.background.tonal,
168
+ },
169
+ this.isStartPart && styles.dayStartContainer,
170
+ this.isEnd && styles.dayEndContainer,
171
+ ]}>
172
+ <TouchableOpacity
173
+ style={[
174
+ styles.day,
175
+ {
176
+ width: itemWidth,
177
+ paddingVertical: !!price ? Spacing.XS : Spacing.S,
178
+ },
179
+ (this.isStart || this.isEnd) && {
180
+ backgroundColor: theme.colors.primary,
181
+ },
182
+ !!price && {height: scaleSize(48)},
183
+ {height: size.height},
184
+ ]}
185
+ disabled={!(this.isValid && this.isInScope)}
186
+ onPress={this.chooseDay}>
187
+ {this.lunarDate && this.showLunar && !!text && (
188
+ <RNText
189
+ style={{
190
+ position: 'absolute',
191
+ top: Spacing.XS,
192
+ right: Spacing.XS,
193
+ color: lunarTextColor,
194
+ }}>
195
+ {this.lunarDate.lunarDay === 1
196
+ ? `${this.lunarDate.lunarDay}/${this.lunarDate.lunarMonth}`
197
+ : this.lunarDate.lunarDay}
198
+ </RNText>
199
+ )}
200
+ <Text typography={'header_s_semibold'} color={textColor}>
201
+ {text}
202
+ </Text>
203
+ {!!price && (
204
+ <Text
205
+ typography={'description_xs_regular'}
206
+ color={priceColor}>
207
+ {price}
208
+ </Text>
209
+ )}
210
+ </TouchableOpacity>
211
+ </View>
212
+ </View>
213
+ );
214
+ }}
215
+ </ContainerContext.Consumer>
216
+ );
90
217
  }
91
-
92
- return (
93
- <View style={[styles.dayContainer, {width: itemWidth}]}>
94
- <View
95
- style={[
96
- isMid && {backgroundColor: theme.colors.background.tonal},
97
- isStartPart && styles.dayStartContainer,
98
- isEnd && styles.dayEndContainer,
99
- ]}>
100
- <TouchableOpacity
101
- style={[
102
- styles.day,
103
- {
104
- width: itemWidth,
105
- paddingVertical: !!price ? Spacing.XS : Spacing.S,
106
- },
107
- (isStart || isEnd) && {backgroundColor: theme.colors.primary},
108
- !!price && {height: scaleSize(48)},
109
- {height: size.height},
110
- ]}
111
- disabled={!(isValid && isInScope)}
112
- onPress={chooseDay}>
113
- {lunarDate && showLunar && !!date && (
114
- <RNText
115
- style={{
116
- position: 'absolute',
117
- top: Spacing.XS,
118
- right: Spacing.XS,
119
- color: lunarTextColor,
120
- }}>
121
- {lunarDate.lunarDay === 1
122
- ? `${lunarDate.lunarDay}/${lunarDate.lunarMonth}`
123
- : lunarDate.lunarDay}
124
- </RNText>
125
- )}
126
- <Text typography="header_s_semibold" color={textColor}>
127
- {date ? date.date() : ''}
128
- </Text>
129
- {!!price && (
130
- <Text typography="description_xs_regular" color={priceColor}>
131
- {price}
132
- </Text>
133
- )}
134
- </TouchableOpacity>
135
- </View>
136
- </View>
137
- );
138
- };
218
+ }
139
219
 
140
220
  export default Day;
@@ -132,13 +132,6 @@ function LunarSolarConverter(this: any) {
132
132
  lunarMonth: number;
133
133
  lunarDay: number;
134
134
  }) {
135
- // year range check: only support lunar calendar from 1900
136
- const minLunarYear = 1900;
137
- // using converter data, maximum supported lunar year
138
- const maxLunarYear = this.lunar_month_days[0] + this.lunar_month_days.length - 1;
139
- if (lunar.lunarYear < minLunarYear || lunar.lunarYear > maxLunarYear) {
140
- throw new Error(`LunarToSolar: year ${lunar.lunarYear} out of supported range ${minLunarYear}-${maxLunarYear}`);
141
- }
142
135
  const days =
143
136
  this.lunar_month_days[lunar.lunarYear - this.lunar_month_days[0]];
144
137
  const leap = this.GetBitInt(days, 4, 13);
@@ -170,13 +163,6 @@ function LunarSolarConverter(this: any) {
170
163
  solarMonth: number;
171
164
  solarDay: number;
172
165
  }) {
173
- // year range check: only support solar dates from 1900
174
- const minSolarYear = 1900;
175
- // using converter data, maximum supported solar year
176
- const maxSolarYear = this.solar_1_1[0] + this.solar_1_1.length - 1;
177
- if (solar.solarYear < minSolarYear || solar.solarYear > maxSolarYear) {
178
- throw new Error(`SolarToLunar: year ${solar.solarYear} out of supported range ${minSolarYear}-${maxSolarYear}`);
179
- }
180
166
  // @ts-ignore
181
167
  const lunar = new Lunar();
182
168
  let index = solar.solarYear - this.solar_1_1[0];
package/Month.tsx CHANGED
@@ -1,117 +1,114 @@
1
- import React, {useContext, useMemo} from 'react';
1
+ import React, {PureComponent} from 'react';
2
+
2
3
  import {View} from 'react-native';
3
- import moment, {Moment} from 'moment';
4
+ import moment from 'moment';
5
+ import Moment from 'moment';
4
6
  import Day from './Day';
5
7
  import {scaleSize, Spacing} from '@momo-kits/foundation';
6
8
  import {MonthProps} from './types';
7
9
  import {ContainerContext} from './index';
8
10
 
9
- const Month: React.FC<MonthProps> = ({
10
- dateList,
11
- holidays,
12
- priceListDate,
13
- minDate,
14
- maxDate,
15
- startDate,
16
- endDate,
17
- isShowLunar,
18
- isDoubleDateMode,
19
- disabledDays,
20
- month,
21
- ...props
22
- }) => {
23
- const rows = useMemo(() => {
24
- const rowCount = dateList.length / 7;
25
- const rowArray = new Array(rowCount).fill('');
26
- return rowArray.map((_, i) => dateList.slice(i * 7, i * 7 + 7));
27
- }, [dateList]);
11
+ export default class Month extends PureComponent<MonthProps> {
12
+ rowArray;
13
+ temp;
14
+ constructor(props: MonthProps) {
15
+ super(props);
16
+ const {dateList} = props;
17
+ this.rowArray = new Array(dateList.length / 7).fill('');
18
+ this.temp = this.rowArray.map((item, i) =>
19
+ dateList.slice(i * 7, i * 7 + 7),
20
+ );
21
+ }
28
22
 
29
- const findHoliday = (date?: Moment) => {
30
- if (!date || !holidays || holidays.length === 0) {
31
- return null;
23
+ findHoliday = (date: Moment.Moment) => {
24
+ const {holidays} = this.props;
25
+ if (date && holidays && holidays.length > 0) {
26
+ const day = date.date();
27
+ const month = date.month() + 1;
28
+ return holidays.find(item => item.day === day && item.month === month);
32
29
  }
33
- const day = date.date();
34
- const monthNum = date.month() + 1;
35
- return (
36
- holidays.find(item => item.day === day && item.month === monthNum) || null
37
- );
30
+ return null;
38
31
  };
39
32
 
40
- const checkHoliday = (date?: Moment) => {
41
- const holiday = findHoliday(date);
33
+ checkHoliday = (date: moment.Moment) => {
34
+ const holiday = this.findHoliday(date);
42
35
  return {
43
36
  isSolarHoliday: !!(holiday && !holiday.lunar),
44
37
  isLunarHoliday: !!(holiday && holiday.lunar),
45
38
  };
46
39
  };
47
40
 
48
- const size = useContext(ContainerContext);
49
-
50
- // Precompute disabled dates set for fast lookup (format: YYYY-MM-DD)
51
- const disabledSet = useMemo(
52
- () => new Set((disabledDays || []).map(d => moment(d).format('YYYY-MM-DD'))),
53
- [disabledDays]
54
- );
55
-
56
- if (!month) {
57
- return <View />;
58
- }
41
+ renderDayRow = (dayList: any[], index: number) => (
42
+ <View
43
+ style={{
44
+ flexDirection: 'row',
45
+ alignItems: 'center',
46
+ marginBottom: Spacing.XS,
47
+ height: scaleSize(48),
48
+ }}
49
+ key={`row${index}`}>
50
+ {dayList.map((item, i) => {
51
+ const keyDay = moment(item.date).format('YYYY-MM-DD');
52
+ const priceInfo = this.props?.priceListDate?.[keyDay];
53
+ const {
54
+ minDate,
55
+ maxDate,
56
+ startDate,
57
+ endDate,
58
+ isShowLunar,
59
+ isDoubleDateMode,
60
+ disabledDays,
61
+ } = this.props;
59
62
 
60
- return (
61
- <View style={{width: size.width}}>
62
- <View
63
- style={{
64
- paddingHorizontal: Spacing.S,
65
- width: '100%',
66
- alignItems: 'center',
67
- }}>
68
- {rows.map((dayList, rowIndex) => (
69
- <View
70
- key={`row${rowIndex}`}
71
- style={{
72
- flexDirection: 'row',
73
- alignItems: 'center',
74
- marginBottom: Spacing.XS,
75
- height: scaleSize(48),
76
- }}>
77
- {dayList.map((item, i) => {
78
- const {isEmpty, date: dateMoment, lunarDate} = item;
79
- // compute key for price/disabled only if not empty
80
- const keyDay = !isEmpty && dateMoment
81
- ? dateMoment.format('YYYY-MM-DD')
82
- : '';
83
- const priceInfo = keyDay ? priceListDate?.[keyDay] : undefined;
84
- const isDisabled = keyDay
85
- ? disabledSet.has(keyDay)
86
- : false;
63
+ const dateItem = new Date(item.date);
64
+ const mappedDisabledArray = disabledDays?.map(item => String(item));
65
+ const isDisabled = mappedDisabledArray?.includes(String(dateItem));
87
66
 
88
- return (
89
- <Day
90
- {...props}
91
- minDate={minDate}
92
- maxDate={maxDate}
93
- isShowLunar={isShowLunar}
94
- isDoubleDateMode={isDoubleDateMode}
95
- startDate={startDate}
96
- endDate={endDate}
97
- {...checkHoliday(dateMoment)}
98
- date={dateMoment}
99
- havePriceList={!!priceListDate}
100
- lunarDate={lunarDate}
101
- isEmpty={isEmpty}
102
- key={`day${i}`}
103
- index={i}
104
- price={priceInfo?.priceAsString}
105
- isBestPrice={priceInfo?.isBestPrice}
106
- isDisabled={isDisabled}
107
- />
108
- );
109
- })}
110
- </View>
111
- ))}
112
- </View>
67
+ return (
68
+ <Day
69
+ {...this.props}
70
+ minDate={minDate}
71
+ maxDate={maxDate}
72
+ isShowLunar={isShowLunar}
73
+ isDoubleDateMode={isDoubleDateMode}
74
+ startDate={startDate}
75
+ endDate={endDate}
76
+ {...this.checkHoliday(item.date)}
77
+ date={item.date}
78
+ havePriceList={!!this.props.priceListDate}
79
+ lunarDate={item.lunarDate}
80
+ empty={item.empty}
81
+ key={`day${i.toString()}`}
82
+ index={i}
83
+ price={priceInfo?.priceAsString}
84
+ isBestPrice={priceInfo?.isBestPrice}
85
+ isDisabled={isDisabled}
86
+ />
87
+ );
88
+ })}
113
89
  </View>
114
90
  );
115
- };
116
91
 
117
- export default React.memo(Month);
92
+ render() {
93
+ const {month} = this.props;
94
+ if (month) {
95
+ return (
96
+ <ContainerContext.Consumer>
97
+ {size => (
98
+ <View style={{width: size.width}}>
99
+ <View
100
+ style={{
101
+ paddingHorizontal: Spacing.S,
102
+ width: '100%',
103
+ alignItems: 'center',
104
+ }}>
105
+ {this.temp.map((item, i) => this.renderDayRow(item, i))}
106
+ </View>
107
+ </View>
108
+ )}
109
+ </ContainerContext.Consumer>
110
+ );
111
+ }
112
+ return <View />;
113
+ }
114
+ }