@hero-design/rn 8.89.0-alpha.0 → 8.89.0

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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +13 -0
  2. package/CHANGELOG.md +10 -0
  3. package/es/index.js +1441 -1126
  4. package/lib/index.js +1441 -1127
  5. package/package.json +2 -3
  6. package/src/components/Calendar/CalendarRange.tsx +337 -0
  7. package/src/components/Calendar/CalendarRangeConnector.tsx +68 -0
  8. package/src/components/Calendar/CalendarRowItem.tsx +14 -3
  9. package/src/components/Calendar/StyledCalendar.tsx +23 -9
  10. package/src/components/Calendar/__tests__/CalendarRange.spec.tsx +284 -0
  11. package/src/components/Calendar/__tests__/CalendarRangeConnector.spec.tsx +73 -0
  12. package/src/components/Calendar/__tests__/__snapshots__/CalendarRangeConnector.spec.tsx.snap +632 -0
  13. package/src/components/Calendar/__tests__/__snapshots__/CalendarRowItem.spec.tsx.snap +45 -20
  14. package/src/components/Calendar/__tests__/helper.spec.ts +110 -1
  15. package/src/components/Calendar/helpers.ts +75 -0
  16. package/src/components/Calendar/index.tsx +19 -15
  17. package/src/components/RichTextEditor/RichTextEditor.tsx +14 -20
  18. package/src/components/RichTextEditor/__tests__/__snapshots__/RichTextEditor.spec.tsx.snap +2 -4
  19. package/src/index.ts +0 -2
  20. package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +9 -4
  21. package/src/theme/components/calendar.ts +6 -1
  22. package/src/theme/global/index.ts +0 -2
  23. package/src/theme/index.ts +0 -2
  24. package/src/types.ts +2 -0
  25. package/stats/8.88.0/rn-stats.html +0 -2
  26. package/stats/8.89.0/rn-stats.html +4842 -0
  27. package/types/components/Calendar/CalendarRowItem.d.ts +3 -1
  28. package/types/components/Calendar/StyledCalendar.d.ts +2 -1
  29. package/types/components/Calendar/helpers.d.ts +15 -0
  30. package/types/components/Calendar/index.d.ts +4 -2
  31. package/types/index.d.ts +2 -2
  32. package/types/theme/components/calendar.d.ts +5 -0
  33. package/types/theme/global/index.d.ts +1 -2
  34. package/types/theme/index.d.ts +2 -2
  35. package/types/types.d.ts +2 -1
  36. package/src/theme/global/colors/ehWorkDark.ts +0 -60
  37. package/types/components/Calendar/CalendarRangeSelectedItem.d.ts +0 -11
  38. package/types/components/Calendar/Sample.d.ts +0 -13
  39. package/types/components/Calendar/hooks/useCalendarLayout.d.ts +0 -7
  40. package/types/components/Calendar/useSetUpCalendar.d.ts +0 -0
  41. package/types/components/CompoundSearch/CompoundSearchHandler.d.ts +0 -31
  42. package/types/components/CompoundSearch/CompoundSearchTextInput.d.ts +0 -60
  43. package/types/components/CompoundSearch/StyledCompoundSearch.d.ts +0 -40
  44. package/types/components/CompoundSearch/index.d.ts +0 -8
  45. package/types/components/CompoundSearch/utils.d.ts +0 -8
  46. package/types/components/FloatingIsland/SingleLine/StyledSingleLine.d.ts +0 -15
  47. package/types/components/FloatingIsland/SingleLine/index.d.ts +0 -24
  48. package/types/test-utils.d.ts +0 -4
  49. package/types/theme/components/compoundSearch.d.ts +0 -36
  50. package/types/theme/global/colors/ehWorkDark.d.ts +0 -48
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn",
3
- "version": "8.89.0-alpha.0",
3
+ "version": "8.89.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -95,6 +95,5 @@
95
95
  "ts-jest": "^29.1.1",
96
96
  "typescript": "^5.7.3"
97
97
  },
98
- "prettier": "prettier-config-hd",
99
- "react-native": "src/index.ts"
98
+ "prettier": "prettier-config-hd"
100
99
  }
@@ -0,0 +1,337 @@
1
+ import {
2
+ MonthYearPickerDialogueAndroid,
3
+ MonthYearPickerViewIOS,
4
+ } from '@hero-design/react-native-month-year-picker';
5
+ import format from 'date-fns/fp/format';
6
+ import React, { useMemo, useState } from 'react';
7
+ import { LayoutChangeEvent, Platform, TouchableOpacity } from 'react-native';
8
+ import { useTheme } from '../../theme';
9
+ import { noop } from '../../utils/functions';
10
+ import Box from '../Box';
11
+ import ContentNavigator from '../ContentNavigator';
12
+ import Icon from '../Icon';
13
+ import Typography from '../Typography';
14
+ import CalendarRowItem from './CalendarRowItem';
15
+ import {
16
+ StyledCalendarDayNameCell,
17
+ StyledCalendarHeader,
18
+ StyledCalendarRow,
19
+ StyledCalendarRowItem,
20
+ StyledContainer,
21
+ StyledDisabledCalendarRowItem,
22
+ } from './StyledCalendar';
23
+ import {
24
+ getValidDate,
25
+ initArray,
26
+ isDateInRange,
27
+ isEqDate,
28
+ setStartOrEndDate,
29
+ } from './helpers';
30
+ import SelectedDate from './CalendarRangeConnector';
31
+
32
+ const DAYS_OF_WEEK = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
33
+ // Sunday first column => 0
34
+ // Sunday last column => 1
35
+ const WEEK_INDEX_OFFSET = 1;
36
+ const SUNDAY_INDEX = 6;
37
+
38
+ // Always render 7 rows x 6 items for consistent layout
39
+ const TOTAL_DATES_ITEMS = 7 * 6;
40
+
41
+ type ParsedMaskedDate = {
42
+ [key: string]: boolean;
43
+ };
44
+
45
+ export type CalendarDateRange = {
46
+ startDate?: Date;
47
+ endDate?: Date;
48
+ };
49
+
50
+ export interface CalendarProps {
51
+ value?: CalendarDateRange;
52
+ visibleDate: Date;
53
+ minDate?: Date;
54
+ maxDate?: Date;
55
+ onChange?: (dateRange: CalendarDateRange) => void;
56
+ onPreviousPress?: () => void;
57
+ onNextPress?: () => void;
58
+ onTitlePress?: () => void;
59
+ markedDates?: Date[];
60
+ testID?: string;
61
+ onMonthChange?: (date: Date) => void;
62
+ onToggleMonthPicker?: (visible: boolean) => void;
63
+ monthPickerConfirmLabel?: string;
64
+ monthPickerCancelLabel?: string;
65
+ }
66
+
67
+ const CalendarRange = ({
68
+ value,
69
+ visibleDate,
70
+ onChange,
71
+ onPreviousPress = noop,
72
+ onNextPress = noop,
73
+ onTitlePress = noop,
74
+ minDate,
75
+ maxDate,
76
+ markedDates = [],
77
+ testID,
78
+ onMonthChange = noop,
79
+ onToggleMonthPicker = noop,
80
+ monthPickerConfirmLabel,
81
+ monthPickerCancelLabel,
82
+ }: CalendarProps) => {
83
+ const theme = useTheme();
84
+ const currentMonth = visibleDate.getMonth();
85
+ const currentYear = visibleDate.getFullYear();
86
+ const now = new Date();
87
+ const parsedMaskedDate: ParsedMaskedDate = markedDates.reduce(
88
+ (current, markedDate) => ({
89
+ ...current,
90
+ [markedDate.toDateString()]: true,
91
+ }),
92
+ {}
93
+ );
94
+ const [monthPickerVisible, setMonthPickerVisible] = useState(false);
95
+ const [contentHeight, setContentHeight] = useState(0);
96
+ const [contentWidth, setContentWidth] = useState(0);
97
+ const calendarItemWidth = useMemo(
98
+ () =>
99
+ contentWidth > 0
100
+ ? Math.floor(
101
+ (contentWidth - theme.__hd__.calendar.space.cellPadding) / 7
102
+ )
103
+ : undefined,
104
+ [contentWidth, theme]
105
+ );
106
+
107
+ const useMonthPicker = onMonthChange !== noop;
108
+
109
+ const firstDateOfMonth = new Date(currentYear, currentMonth, 1);
110
+ const lastDateOfMonth = new Date(currentYear, currentMonth + 1, 0);
111
+
112
+ const lastDateOfPreviousMonth = new Date(currentYear, currentMonth, 0);
113
+
114
+ // Index of day in week is shifted by 1 due to Sunday is the last column
115
+ const firstDayWeekIndexOfMonth =
116
+ firstDateOfMonth.getDay() === 0
117
+ ? SUNDAY_INDEX
118
+ : firstDateOfMonth.getDay() - WEEK_INDEX_OFFSET;
119
+
120
+ const lastDayIndexOfCurrentMonth = lastDateOfMonth.getDate();
121
+ const lastDayIndexOfPreviousMonth = lastDateOfPreviousMonth.getDate();
122
+
123
+ const daysOfPreviousMonth = initArray(firstDayWeekIndexOfMonth, (index) => {
124
+ const reversedIndex = firstDayWeekIndexOfMonth - index - 1;
125
+ const count = lastDayIndexOfPreviousMonth - reversedIndex;
126
+ return getValidDate(
127
+ new Date(currentYear, currentMonth - 1, count),
128
+ minDate,
129
+ maxDate
130
+ );
131
+ });
132
+
133
+ const daysOfCurrentMonth = initArray(lastDayIndexOfCurrentMonth, (index) =>
134
+ getValidDate(
135
+ new Date(currentYear, currentMonth, index + 1),
136
+ minDate,
137
+ maxDate
138
+ )
139
+ );
140
+
141
+ const daysOfNextMonth = initArray(
142
+ TOTAL_DATES_ITEMS -
143
+ (daysOfPreviousMonth.length + daysOfCurrentMonth.length),
144
+ (index) =>
145
+ getValidDate(
146
+ new Date(currentYear, currentMonth + 1, index + 1),
147
+ minDate,
148
+ maxDate
149
+ )
150
+ );
151
+
152
+ const disablePrevButton =
153
+ minDate === undefined
154
+ ? false
155
+ : !daysOfPreviousMonth.some((date) => date !== undefined) &&
156
+ minDate >= firstDateOfMonth;
157
+
158
+ const disableNextButton =
159
+ maxDate === undefined
160
+ ? false
161
+ : !daysOfNextMonth.some((date) => date !== undefined) ||
162
+ maxDate <= lastDateOfMonth;
163
+
164
+ const onDateChange = (date: Date) => {
165
+ const newDateRange = setStartOrEndDate({
166
+ date,
167
+ startDate: value?.startDate,
168
+ endDate: value?.endDate,
169
+ });
170
+
171
+ onChange?.(newDateRange);
172
+ };
173
+
174
+ const renderDateCell = ({
175
+ date,
176
+ isCurrentMonth,
177
+ }: {
178
+ date?: Date;
179
+ isCurrentMonth: boolean;
180
+ }) => {
181
+ if (!date) {
182
+ return (
183
+ <StyledDisabledCalendarRowItem
184
+ themeItemWidth={calendarItemWidth}
185
+ testID="calendar-disabled-cell"
186
+ />
187
+ );
188
+ }
189
+
190
+ if (isEqDate(value?.startDate, date) || isEqDate(value?.endDate, date)) {
191
+ return (
192
+ <SelectedDate
193
+ isStart={isEqDate(date, value?.startDate)}
194
+ showConnector={!!value?.startDate && !!value?.endDate}
195
+ key={date.toDateString()}
196
+ date={date}
197
+ onPress={() => onDateChange(date)}
198
+ marked={parsedMaskedDate[date.toDateString()]}
199
+ itemWidth={calendarItemWidth}
200
+ />
201
+ );
202
+ }
203
+
204
+ return (
205
+ <CalendarRowItem
206
+ itemWidth={calendarItemWidth}
207
+ key={date.toDateString()}
208
+ date={date}
209
+ isCurrent={isEqDate(now, date)}
210
+ isSelected={
211
+ isEqDate(value?.startDate, date) || isEqDate(value?.endDate, date)
212
+ }
213
+ isHighlighted={isDateInRange({ date, range: value })}
214
+ onPress={() => onDateChange(date)}
215
+ marked={parsedMaskedDate[date.toDateString()]}
216
+ textIntent={isCurrentMonth ? undefined : 'subdued'}
217
+ />
218
+ );
219
+ };
220
+
221
+ const onLayout = (e: LayoutChangeEvent) => {
222
+ const { width, height } = e.nativeEvent.layout;
223
+ setContentHeight(height);
224
+ setContentWidth(width);
225
+ };
226
+
227
+ return (
228
+ <StyledContainer testID={testID}>
229
+ <StyledCalendarHeader>
230
+ <ContentNavigator
231
+ value={
232
+ !useMonthPicker ? (
233
+ format('MMMM yyyy', visibleDate)
234
+ ) : (
235
+ <TouchableOpacity
236
+ testID="calendar-month-picker"
237
+ onPress={() => {
238
+ onToggleMonthPicker?.(!monthPickerVisible);
239
+ setMonthPickerVisible(!monthPickerVisible);
240
+ }}
241
+ >
242
+ <Box
243
+ flexDirection="row"
244
+ justifyContent="center"
245
+ alignItems="center"
246
+ >
247
+ <Typography.Title
248
+ level="h5"
249
+ style={{
250
+ textAlign: 'center',
251
+ marginRight:
252
+ theme.__hd__.calendar.space.headerMarginRight,
253
+ }}
254
+ >
255
+ {format('MMMM yyyy', visibleDate)}
256
+ </Typography.Title>
257
+
258
+ <Icon
259
+ icon={monthPickerVisible ? 'arrow-up' : 'arrow-down'}
260
+ size="small"
261
+ />
262
+ </Box>
263
+ </TouchableOpacity>
264
+ )
265
+ }
266
+ onPreviousPress={onPreviousPress}
267
+ onNextPress={onNextPress}
268
+ onPress={useMonthPicker ? undefined : onTitlePress}
269
+ previousDisabled={disablePrevButton}
270
+ nextDisabled={disableNextButton}
271
+ fontSize="large"
272
+ />
273
+ </StyledCalendarHeader>
274
+ {Platform.OS === 'ios' && monthPickerVisible ? (
275
+ <Box style={{ overflow: 'hidden' }}>
276
+ <MonthYearPickerViewIOS
277
+ value={value?.startDate}
278
+ minimumDate={minDate}
279
+ maximumDate={maxDate}
280
+ onChange={onMonthChange}
281
+ style={{
282
+ height:
283
+ contentHeight +
284
+ theme.__hd__.calendar.space.iosPickerMarginVertical * 2,
285
+ marginVertical:
286
+ -theme.__hd__.calendar.space.iosPickerMarginVertical,
287
+ width: contentWidth,
288
+ }}
289
+ />
290
+ </Box>
291
+ ) : (
292
+ <Box onLayout={onLayout}>
293
+ <StyledCalendarRow>
294
+ {DAYS_OF_WEEK.map((day) => (
295
+ <StyledCalendarRowItem key={day}>
296
+ <StyledCalendarDayNameCell themeItemWidth={calendarItemWidth}>
297
+ <Typography.Body variant="small">{day}</Typography.Body>
298
+ </StyledCalendarDayNameCell>
299
+ </StyledCalendarRowItem>
300
+ ))}
301
+ </StyledCalendarRow>
302
+ <StyledCalendarRow>
303
+ {daysOfPreviousMonth.map((date) =>
304
+ renderDateCell({ date, isCurrentMonth: false })
305
+ )}
306
+ {daysOfCurrentMonth.map((date) =>
307
+ renderDateCell({ date, isCurrentMonth: true })
308
+ )}
309
+ {daysOfNextMonth.map((date) =>
310
+ renderDateCell({ date, isCurrentMonth: false })
311
+ )}
312
+ </StyledCalendarRow>
313
+
314
+ {Platform.OS === 'android' && monthPickerVisible && (
315
+ <MonthYearPickerDialogueAndroid
316
+ doneButtonLabel={monthPickerConfirmLabel}
317
+ cancelButtonLabel={monthPickerCancelLabel}
318
+ value={value?.startDate}
319
+ minimumDate={minDate}
320
+ maximumDate={maxDate}
321
+ onChange={(action, date) => {
322
+ setMonthPickerVisible(false);
323
+ onToggleMonthPicker?.(false);
324
+
325
+ if (action === 'dateSetAction' && !!date) {
326
+ onMonthChange(date);
327
+ }
328
+ }}
329
+ />
330
+ )}
331
+ </Box>
332
+ )}
333
+ </StyledContainer>
334
+ );
335
+ };
336
+
337
+ export default CalendarRange;
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import {
4
+ StyledCalendarCell,
5
+ StyledMark,
6
+ StyledCalendarRowItem,
7
+ } from './StyledCalendar';
8
+ import Typography from '../Typography';
9
+ import Box from '../Box';
10
+
11
+ interface SelectedDateProps {
12
+ date: Date;
13
+ onPress?: () => void;
14
+ marked?: boolean;
15
+ isStart?: boolean;
16
+ showConnector?: boolean;
17
+ itemWidth?: number;
18
+ }
19
+
20
+ const SelectedDate = ({
21
+ date,
22
+ onPress,
23
+ marked,
24
+ isStart,
25
+ showConnector,
26
+ itemWidth,
27
+ }: SelectedDateProps) => {
28
+ return (
29
+ <StyledCalendarRowItem
30
+ themeItemWidth={itemWidth}
31
+ testID="calendar-date-cell"
32
+ >
33
+ {showConnector ? (
34
+ <Box
35
+ testID="range-connector"
36
+ style={{
37
+ width: '50%',
38
+ height: '100%',
39
+ ...StyleSheet.absoluteFillObject,
40
+ transform: isStart
41
+ ? [
42
+ {
43
+ translateX: itemWidth ? itemWidth / 2 : 0,
44
+ },
45
+ ]
46
+ : undefined,
47
+ }}
48
+ bgColor="highlightedSurface"
49
+ />
50
+ ) : null}
51
+
52
+ <StyledCalendarCell
53
+ testID="selected-date-cell"
54
+ variant="selected"
55
+ onPress={onPress}
56
+ >
57
+ <Typography.Body variant="small" intent="inverted">
58
+ {date ? date.getDate() : ''}
59
+ </Typography.Body>
60
+ {marked ? (
61
+ <StyledMark testID="calendar-date-mark" variant="inverted" />
62
+ ) : null}
63
+ </StyledCalendarCell>
64
+ </StyledCalendarRowItem>
65
+ );
66
+ };
67
+
68
+ export default SelectedDate;
@@ -6,9 +6,14 @@ import {
6
6
  StyledMark,
7
7
  } from './StyledCalendar';
8
8
 
9
- const getCellVariant = (isSelected = false, isCurrent = false) => {
9
+ export const getCellVariant = (
10
+ isSelected = false,
11
+ isCurrent = false,
12
+ isHighlighted = false
13
+ ) => {
10
14
  if (isSelected) return 'selected';
11
15
  if (isCurrent) return 'current';
16
+ if (isHighlighted) return 'highlighted';
12
17
 
13
18
  return 'default';
14
19
  };
@@ -21,6 +26,7 @@ export interface CalendarRowItemProps {
21
26
  textIntent?: 'body' | 'subdued';
22
27
  marked?: boolean;
23
28
  itemWidth?: number;
29
+ isHighlighted?: boolean;
24
30
  }
25
31
 
26
32
  const CalendarRowItem = ({
@@ -31,10 +37,15 @@ const CalendarRowItem = ({
31
37
  textIntent = 'body',
32
38
  marked = false,
33
39
  itemWidth,
40
+ isHighlighted,
34
41
  }: CalendarRowItemProps) => (
35
- <StyledCalendarRowItem testID="calendar-date-cell" themeItemWidth={itemWidth}>
42
+ <StyledCalendarRowItem
43
+ testID="calendar-date-cell"
44
+ themeItemWidth={itemWidth}
45
+ isHighlighted={isHighlighted}
46
+ >
36
47
  <StyledCalendarCell
37
- variant={getCellVariant(isSelected, isCurrent)}
48
+ variant={getCellVariant(isSelected, isCurrent, isHighlighted)}
38
49
  onPress={onPress}
39
50
  >
40
51
  <Typography.Body
@@ -24,36 +24,49 @@ const StyledCalendarDayNameCell = styled(View)<
24
24
  }));
25
25
 
26
26
  const StyledCalendarCell = styled(TouchableOpacity)<{
27
- variant?: 'default' | 'current' | 'selected';
27
+ variant?: 'default' | 'current' | 'selected' | 'highlighted';
28
28
  themeItemWidth?: number;
29
- }>(({ theme, variant = 'default', themeItemWidth }) => ({
29
+ }>(({ theme, variant = 'default' }) => ({
30
30
  borderColor: theme.__hd__.calendar.colors.border,
31
31
  borderWidth: variant === 'current' ? 1 : 0,
32
- borderRadius: theme.__hd__.calendar.radii.default,
32
+ borderRadius:
33
+ variant === 'highlighted' ? undefined : theme.__hd__.calendar.radii.default,
33
34
  alignItems: 'center',
34
35
  justifyContent: 'center',
35
36
  backgroundColor:
36
- variant === 'selected' ? theme.__hd__.calendar.colors.primary : undefined,
37
- width: themeItemWidth || theme.__hd__.calendar.sizes.cellCircleWidth,
38
- height: theme.__hd__.calendar.sizes.cellCircleHeight,
37
+ variant === 'selected'
38
+ ? theme.__hd__.calendar.colors.rowItem.selected
39
+ : 'transparent',
40
+ width: '100%',
41
+ height: '100%',
42
+ maxWidth: theme.__hd__.calendar.sizes.cellWidth,
43
+ maxHeight: theme.__hd__.calendar.sizes.cellHeight,
39
44
  }));
40
45
 
41
46
  const StyledCalendarRow = styled(View)<ViewProps>(({ theme }) => ({
42
47
  flexDirection: 'row',
43
48
  paddingHorizontal: theme.__hd__.calendar.space.rowVerticalPadding,
44
49
  flexWrap: 'wrap',
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
45
52
  }));
46
53
 
47
54
  const StyledCalendarRowItem = styled(View)<
48
55
  ViewProps & {
49
56
  themeItemWidth?: number;
57
+ isHighlighted?: boolean;
50
58
  }
51
- >(({ theme, themeItemWidth }) => ({
59
+ >(({ theme, themeItemWidth, isHighlighted }) => ({
52
60
  flexBasis: `${Math.floor(100.0 / 7.0)}%`,
61
+ lineHeight: 0,
53
62
  alignItems: 'center',
54
63
  width: themeItemWidth || theme.__hd__.calendar.sizes.cellWidth,
55
- height: theme.__hd__.calendar.sizes.cellHeight,
64
+ height: themeItemWidth || theme.__hd__.calendar.sizes.cellHeight,
65
+ maxHeight: theme.__hd__.calendar.sizes.cellHeight,
56
66
  justifyContent: 'center',
67
+ backgroundColor: isHighlighted
68
+ ? theme.__hd__.calendar.colors.rowItem.highlighted
69
+ : undefined,
57
70
  }));
58
71
 
59
72
  const StyledDisabledCalendarRowItem = styled(View)<
@@ -64,7 +77,8 @@ const StyledDisabledCalendarRowItem = styled(View)<
64
77
  flexBasis: `${Math.floor(100.0 / 7.0)}%`,
65
78
  alignItems: 'center',
66
79
  width: themeItemWidth || theme.__hd__.calendar.sizes.cellWidth,
67
- height: theme.__hd__.calendar.sizes.cellHeight,
80
+ height: themeItemWidth || theme.__hd__.calendar.sizes.cellHeight,
81
+ maxHeight: theme.__hd__.calendar.sizes.cellHeight,
68
82
  }));
69
83
 
70
84
  const StyledMark = styled(View)<{ variant: 'primary' | 'inverted' }>(