@utilitywarehouse/hearth-react-native 0.3.1 → 0.4.1

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 (274) hide show
  1. package/.storybook/preview.tsx +3 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/CHANGELOG.md +16 -0
  5. package/build/components/CurrencyInput/CurrencyInput.js +1 -1
  6. package/build/components/DatePicker/DatePicker.context.d.ts +19 -0
  7. package/build/components/DatePicker/DatePicker.context.js +3 -0
  8. package/build/components/DatePicker/DatePicker.d.ts +19 -0
  9. package/build/components/DatePicker/DatePicker.js +479 -0
  10. package/build/components/DatePicker/DatePicker.props.d.ts +125 -0
  11. package/build/components/DatePicker/DatePicker.props.js +1 -0
  12. package/build/components/DatePicker/DatePickerCalendar.d.ts +2 -0
  13. package/build/components/DatePicker/DatePickerCalendar.js +28 -0
  14. package/build/components/DatePicker/DatePickerDay.d.ts +11 -0
  15. package/build/components/DatePicker/DatePickerDay.js +242 -0
  16. package/build/components/DatePicker/DatePickerDays.d.ts +2 -0
  17. package/build/components/DatePicker/DatePickerDays.js +157 -0
  18. package/build/components/DatePicker/DatePickerFooter.d.ts +2 -0
  19. package/build/components/DatePicker/DatePickerFooter.js +23 -0
  20. package/build/components/DatePicker/DatePickerHeader/DatePickerHeader.props.d.ts +8 -0
  21. package/build/components/DatePicker/DatePickerHeader/DatePickerHeader.props.js +1 -0
  22. package/build/components/DatePicker/DatePickerHeader/DatePickerMonthButton.d.ts +2 -0
  23. package/build/components/DatePicker/DatePickerHeader/DatePickerMonthButton.js +14 -0
  24. package/build/components/DatePicker/DatePickerHeader/DatePickerNextButton.d.ts +2 -0
  25. package/build/components/DatePicker/DatePickerHeader/DatePickerNextButton.js +32 -0
  26. package/build/components/DatePicker/DatePickerHeader/DatePickerPrevButton.d.ts +2 -0
  27. package/build/components/DatePicker/DatePickerHeader/DatePickerPrevButton.js +32 -0
  28. package/build/components/DatePicker/DatePickerHeader/DatePickerSelectors.d.ts +6 -0
  29. package/build/components/DatePicker/DatePickerHeader/DatePickerSelectors.js +64 -0
  30. package/build/components/DatePicker/DatePickerHeader/DatePickerTimeButton.d.ts +1 -0
  31. package/build/components/DatePicker/DatePickerHeader/DatePickerTimeButton.js +22 -0
  32. package/build/components/DatePicker/DatePickerHeader/DatePickerYearButton.d.ts +2 -0
  33. package/build/components/DatePicker/DatePickerHeader/DatePickerYearButton.js +25 -0
  34. package/build/components/DatePicker/DatePickerHeader/index.d.ts +3 -0
  35. package/build/components/DatePicker/DatePickerHeader/index.js +30 -0
  36. package/build/components/DatePicker/DatePickerMonths.d.ts +2 -0
  37. package/build/components/DatePicker/DatePickerMonths.js +69 -0
  38. package/build/components/DatePicker/DatePickerWeekdays.d.ts +8 -0
  39. package/build/components/DatePicker/DatePickerWeekdays.js +26 -0
  40. package/build/components/DatePicker/DatePickerYears.d.ts +2 -0
  41. package/build/components/DatePicker/DatePickerYears.js +83 -0
  42. package/build/components/DatePicker/TimePicker.d.ts +3 -0
  43. package/build/components/DatePicker/TimePicker.js +84 -0
  44. package/build/components/DatePicker/enums.d.ts +12 -0
  45. package/build/components/DatePicker/enums.js +12 -0
  46. package/build/components/DatePicker/index.d.ts +4 -0
  47. package/build/components/DatePicker/index.js +3 -0
  48. package/build/components/DatePicker/polyfill.d.ts +0 -0
  49. package/build/components/DatePicker/polyfill.js +22 -0
  50. package/build/components/DatePicker/time-picker/animated-math.d.ts +4 -0
  51. package/build/components/DatePicker/time-picker/animated-math.js +19 -0
  52. package/build/components/DatePicker/time-picker/period-native.d.ts +6 -0
  53. package/build/components/DatePicker/time-picker/period-native.js +17 -0
  54. package/build/components/DatePicker/time-picker/period-picker.d.ts +6 -0
  55. package/build/components/DatePicker/time-picker/period-picker.js +10 -0
  56. package/build/components/DatePicker/time-picker/period-web.d.ts +6 -0
  57. package/build/components/DatePicker/time-picker/period-web.js +21 -0
  58. package/build/components/DatePicker/time-picker/wheel-native.d.ts +8 -0
  59. package/build/components/DatePicker/time-picker/wheel-native.js +19 -0
  60. package/build/components/DatePicker/time-picker/wheel-picker/index.d.ts +2 -0
  61. package/build/components/DatePicker/time-picker/wheel-picker/index.js +2 -0
  62. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.d.ts +16 -0
  63. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.js +97 -0
  64. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.d.ts +21 -0
  65. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.js +88 -0
  66. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.d.ts +23 -0
  67. package/build/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.js +21 -0
  68. package/build/components/DatePicker/time-picker/wheel-web.d.ts +8 -0
  69. package/build/components/DatePicker/time-picker/wheel-web.js +148 -0
  70. package/build/components/DatePicker/time-picker/wheel.d.ts +8 -0
  71. package/build/components/DatePicker/time-picker/wheel.js +10 -0
  72. package/build/components/DatePicker/utils.d.ts +212 -0
  73. package/build/components/DatePicker/utils.js +391 -0
  74. package/build/components/DatePickerInput/DatePickerInput.d.ts +6 -0
  75. package/build/components/DatePickerInput/DatePickerInput.js +126 -0
  76. package/build/components/DatePickerInput/DatePickerInput.props.d.ts +47 -0
  77. package/build/components/DatePickerInput/DatePickerInput.props.js +1 -0
  78. package/build/components/DatePickerInput/DatePickerInputDoneButton.d.ts +8 -0
  79. package/build/components/DatePickerInput/DatePickerInputDoneButton.js +19 -0
  80. package/build/components/DatePickerInput/DatePickerInputDoneButton.web.d.ts +5 -0
  81. package/build/components/DatePickerInput/DatePickerInputDoneButton.web.js +5 -0
  82. package/build/components/DatePickerInput/index.d.ts +2 -0
  83. package/build/components/DatePickerInput/index.js +1 -0
  84. package/build/components/Input/InputField.d.ts +1 -1
  85. package/build/components/Input/InputField.js +1 -1
  86. package/build/components/Input/InputSlot.d.ts +1 -1
  87. package/build/components/Input/InputSlot.js +3 -3
  88. package/build/components/UnstyledIconButton/UnstyledIconButton.js +2 -2
  89. package/build/components/UnstyledIconButton/UnstyledIconButtonRoot.js +1 -1
  90. package/build/components/index.d.ts +2 -0
  91. package/build/components/index.js +2 -0
  92. package/build/core/themes.d.ts +482 -0
  93. package/build/core/themes.js +31 -0
  94. package/build/hooks/index.d.ts +4 -3
  95. package/build/hooks/index.js +4 -3
  96. package/build/hooks/usePrevious.d.ts +1 -0
  97. package/build/hooks/usePrevious.js +8 -0
  98. package/build/hooks/useTheme.d.ts +2 -1
  99. package/build/hooks/useTheme.js +2 -2
  100. package/build/legacyTokens/common/brand.d.ts +16 -0
  101. package/build/legacyTokens/common/brand.js +17 -0
  102. package/build/legacyTokens/common/index.d.ts +8 -0
  103. package/build/legacyTokens/common/index.js +9 -0
  104. package/build/legacyTokens/common/service.d.ts +20 -0
  105. package/build/legacyTokens/common/service.js +21 -0
  106. package/build/legacyTokens/dark/apple.d.ts +28 -0
  107. package/build/legacyTokens/dark/apple.js +29 -0
  108. package/build/legacyTokens/dark/cyan.d.ts +48 -0
  109. package/build/legacyTokens/dark/cyan.js +49 -0
  110. package/build/legacyTokens/dark/gold.d.ts +44 -0
  111. package/build/legacyTokens/dark/gold.js +45 -0
  112. package/build/legacyTokens/dark/grape.d.ts +28 -0
  113. package/build/legacyTokens/dark/grape.js +29 -0
  114. package/build/legacyTokens/dark/green.d.ts +40 -0
  115. package/build/legacyTokens/dark/green.js +41 -0
  116. package/build/legacyTokens/dark/grey.d.ts +60 -0
  117. package/build/legacyTokens/dark/grey.js +61 -0
  118. package/build/legacyTokens/dark/index.d.ts +40 -0
  119. package/build/legacyTokens/dark/index.js +41 -0
  120. package/build/legacyTokens/dark/pink.d.ts +28 -0
  121. package/build/legacyTokens/dark/pink.js +29 -0
  122. package/build/legacyTokens/dark/purple.d.ts +48 -0
  123. package/build/legacyTokens/dark/purple.js +49 -0
  124. package/build/legacyTokens/dark/red.d.ts +40 -0
  125. package/build/legacyTokens/dark/red.js +41 -0
  126. package/build/legacyTokens/dark/rose.d.ts +28 -0
  127. package/build/legacyTokens/dark/rose.js +29 -0
  128. package/build/legacyTokens/index.d.ts +12 -0
  129. package/build/legacyTokens/index.js +13 -0
  130. package/build/legacyTokens/light/apple.d.ts +28 -0
  131. package/build/legacyTokens/light/apple.js +29 -0
  132. package/build/legacyTokens/light/cyan.d.ts +48 -0
  133. package/build/legacyTokens/light/cyan.js +49 -0
  134. package/build/legacyTokens/light/gold.d.ts +44 -0
  135. package/build/legacyTokens/light/gold.js +45 -0
  136. package/build/legacyTokens/light/grape.d.ts +28 -0
  137. package/build/legacyTokens/light/grape.js +29 -0
  138. package/build/legacyTokens/light/green.d.ts +40 -0
  139. package/build/legacyTokens/light/green.js +41 -0
  140. package/build/legacyTokens/light/grey.d.ts +60 -0
  141. package/build/legacyTokens/light/grey.js +61 -0
  142. package/build/legacyTokens/light/index.d.ts +40 -0
  143. package/build/legacyTokens/light/index.js +41 -0
  144. package/build/legacyTokens/light/pink.d.ts +28 -0
  145. package/build/legacyTokens/light/pink.js +29 -0
  146. package/build/legacyTokens/light/purple.d.ts +48 -0
  147. package/build/legacyTokens/light/purple.js +49 -0
  148. package/build/legacyTokens/light/red.d.ts +40 -0
  149. package/build/legacyTokens/light/red.js +41 -0
  150. package/build/legacyTokens/light/rose.d.ts +32 -0
  151. package/build/legacyTokens/light/rose.js +33 -0
  152. package/build/tokens/components/dark/date-picker.d.ts +1 -0
  153. package/build/tokens/components/dark/date-picker.js +1 -0
  154. package/build/tokens/components/dark/illustrations.d.ts +7 -0
  155. package/build/tokens/components/dark/illustrations.js +6 -0
  156. package/build/tokens/components/dark/index.d.ts +1 -0
  157. package/build/tokens/components/dark/index.js +1 -0
  158. package/build/tokens/components/dark/segmented-control.d.ts +2 -2
  159. package/build/tokens/components/dark/segmented-control.js +2 -2
  160. package/build/tokens/components/dark/table.d.ts +3 -0
  161. package/build/tokens/components/dark/table.js +3 -0
  162. package/build/tokens/components/light/date-picker.d.ts +1 -0
  163. package/build/tokens/components/light/date-picker.js +1 -0
  164. package/build/tokens/components/light/illustrations.d.ts +7 -0
  165. package/build/tokens/components/light/illustrations.js +6 -0
  166. package/build/tokens/components/light/index.d.ts +1 -0
  167. package/build/tokens/components/light/index.js +1 -0
  168. package/build/tokens/components/light/segmented-control.d.ts +2 -2
  169. package/build/tokens/components/light/segmented-control.js +2 -2
  170. package/build/tokens/components/light/table.d.ts +3 -0
  171. package/build/tokens/components/light/table.js +3 -0
  172. package/build/utils/index.d.ts +1 -0
  173. package/build/utils/index.js +1 -0
  174. package/build/utils/isEqual.d.ts +2 -0
  175. package/build/utils/isEqual.js +29 -0
  176. package/chromatic.config.json +6 -0
  177. package/docs/components/AllComponents.web.tsx +43 -1
  178. package/docs/components/ViewWrap.tsx +32 -0
  179. package/docs/components/index.ts +1 -0
  180. package/docs/getting-started.mdx +6 -6
  181. package/package.json +11 -8
  182. package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
  183. package/src/components/DatePicker/DatePicker.context.ts +23 -0
  184. package/src/components/DatePicker/DatePicker.docs.mdx +239 -0
  185. package/src/components/DatePicker/DatePicker.props.ts +139 -0
  186. package/src/components/DatePicker/DatePicker.stories.tsx +98 -0
  187. package/src/components/DatePicker/DatePicker.tsx +669 -0
  188. package/src/components/DatePicker/DatePickerCalendar.tsx +41 -0
  189. package/src/components/DatePicker/DatePickerDay.tsx +302 -0
  190. package/src/components/DatePicker/DatePickerDays.tsx +241 -0
  191. package/src/components/DatePicker/DatePickerFooter.tsx +35 -0
  192. package/src/components/DatePicker/DatePickerHeader/DatePickerHeader.props.ts +10 -0
  193. package/src/components/DatePicker/DatePickerHeader/DatePickerMonthButton.tsx +29 -0
  194. package/src/components/DatePicker/DatePickerHeader/DatePickerNextButton.tsx +46 -0
  195. package/src/components/DatePicker/DatePickerHeader/DatePickerPrevButton.tsx +46 -0
  196. package/src/components/DatePicker/DatePickerHeader/DatePickerSelectors.tsx +96 -0
  197. package/src/components/DatePicker/DatePickerHeader/DatePickerTimeButton.tsx +48 -0
  198. package/src/components/DatePicker/DatePickerHeader/DatePickerYearButton.tsx +50 -0
  199. package/src/components/DatePicker/DatePickerHeader/index.tsx +64 -0
  200. package/src/components/DatePicker/DatePickerMonths.tsx +101 -0
  201. package/src/components/DatePicker/DatePickerWeekdays.tsx +49 -0
  202. package/src/components/DatePicker/DatePickerYears.tsx +119 -0
  203. package/src/components/DatePicker/TimePicker.tsx +141 -0
  204. package/src/components/DatePicker/enums.ts +14 -0
  205. package/src/components/DatePicker/index.ts +13 -0
  206. package/src/components/DatePicker/polyfill.ts +21 -0
  207. package/src/components/DatePicker/time-picker/animated-math.ts +33 -0
  208. package/src/components/DatePicker/time-picker/period-native.tsx +34 -0
  209. package/src/components/DatePicker/time-picker/period-picker.tsx +16 -0
  210. package/src/components/DatePicker/time-picker/period-web.tsx +36 -0
  211. package/src/components/DatePicker/time-picker/wheel-native.tsx +37 -0
  212. package/src/components/DatePicker/time-picker/wheel-picker/index.ts +3 -0
  213. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.tsx +132 -0
  214. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.ts +22 -0
  215. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.tsx +200 -0
  216. package/src/components/DatePicker/time-picker/wheel-web.tsx +181 -0
  217. package/src/components/DatePicker/time-picker/wheel.tsx +18 -0
  218. package/src/components/DatePicker/utils.ts +549 -0
  219. package/src/components/DatePickerInput/DatePickerInput.docs.mdx +237 -0
  220. package/src/components/DatePickerInput/DatePickerInput.props.ts +50 -0
  221. package/src/components/DatePickerInput/DatePickerInput.stories.tsx +178 -0
  222. package/src/components/DatePickerInput/DatePickerInput.tsx +265 -0
  223. package/src/components/DatePickerInput/DatePickerInputDoneButton.tsx +42 -0
  224. package/src/components/DatePickerInput/DatePickerInputDoneButton.web.tsx +7 -0
  225. package/src/components/DatePickerInput/index.ts +2 -0
  226. package/src/components/Icon/Icon.stories.tsx +4 -3
  227. package/src/components/Input/InputField.tsx +0 -2
  228. package/src/components/Input/InputSlot.tsx +14 -3
  229. package/src/components/Modal/Modal.stories.tsx +2 -30
  230. package/src/components/UnstyledIconButton/UnstyledIconButton.tsx +14 -2
  231. package/src/components/UnstyledIconButton/UnstyledIconButtonRoot.tsx +7 -3
  232. package/src/components/index.ts +2 -0
  233. package/src/core/themes.ts +31 -0
  234. package/src/hooks/index.ts +4 -3
  235. package/src/hooks/usePrevious.ts +9 -0
  236. package/src/hooks/useTheme.ts +4 -3
  237. package/src/legacyTokens/common/brand.ts +18 -0
  238. package/src/legacyTokens/common/index.ts +10 -0
  239. package/src/legacyTokens/common/service.ts +22 -0
  240. package/src/legacyTokens/dark/apple.ts +30 -0
  241. package/src/legacyTokens/dark/cyan.ts +50 -0
  242. package/src/legacyTokens/dark/gold.ts +46 -0
  243. package/src/legacyTokens/dark/grape.ts +30 -0
  244. package/src/legacyTokens/dark/green.ts +42 -0
  245. package/src/legacyTokens/dark/grey.ts +62 -0
  246. package/src/legacyTokens/dark/index.ts +42 -0
  247. package/src/legacyTokens/dark/pink.ts +30 -0
  248. package/src/legacyTokens/dark/purple.ts +50 -0
  249. package/src/legacyTokens/dark/red.ts +42 -0
  250. package/src/legacyTokens/dark/rose.ts +30 -0
  251. package/src/legacyTokens/index.ts +14 -0
  252. package/src/legacyTokens/light/apple.ts +30 -0
  253. package/src/legacyTokens/light/cyan.ts +50 -0
  254. package/src/legacyTokens/light/gold.ts +46 -0
  255. package/src/legacyTokens/light/grape.ts +30 -0
  256. package/src/legacyTokens/light/green.ts +42 -0
  257. package/src/legacyTokens/light/grey.ts +62 -0
  258. package/src/legacyTokens/light/index.ts +42 -0
  259. package/src/legacyTokens/light/pink.ts +30 -0
  260. package/src/legacyTokens/light/purple.ts +50 -0
  261. package/src/legacyTokens/light/red.ts +42 -0
  262. package/src/legacyTokens/light/rose.ts +34 -0
  263. package/src/tokens/components/dark/date-picker.ts +1 -0
  264. package/src/tokens/components/dark/illustrations.ts +7 -0
  265. package/src/tokens/components/dark/index.ts +1 -0
  266. package/src/tokens/components/dark/segmented-control.ts +2 -2
  267. package/src/tokens/components/dark/table.ts +3 -0
  268. package/src/tokens/components/light/date-picker.ts +1 -0
  269. package/src/tokens/components/light/illustrations.ts +7 -0
  270. package/src/tokens/components/light/index.ts +1 -0
  271. package/src/tokens/components/light/segmented-control.ts +2 -2
  272. package/src/tokens/components/light/table.ts +3 -0
  273. package/src/utils/index.ts +1 -0
  274. package/src/utils/isEqual.ts +30 -0
@@ -0,0 +1,302 @@
1
+ import { createPressable } from '@gluestack-ui/pressable';
2
+ import React, { memo, useMemo } from 'react';
3
+ import { Pressable, PressableProps, View, ViewStyle } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { isEqual } from '../../utils';
6
+ import { BodyText } from '../BodyText';
7
+ import { CalendarDay, DateType } from './DatePicker.props';
8
+ import { CONTAINER_HEIGHT, WEEKDAYS_HEIGHT } from './enums';
9
+
10
+ interface Props {
11
+ day: CalendarDay;
12
+ onSelectDate: (date: DateType) => void;
13
+ containerHeight?: number;
14
+ weekdaysHeight?: number;
15
+ }
16
+
17
+ export const EmptyDay = React.memo(() => {
18
+ return <View style={styles.dayWrapper} />;
19
+ });
20
+
21
+ const DayPressable = createPressable({
22
+ Root: (props: PressableProps & { states?: { active?: boolean; disabled?: boolean } }) => {
23
+ const { states, style } = props;
24
+ styles.useVariants({ isActive: states?.active });
25
+ return <Pressable {...props} style={[styles.dayContainer, style as ViewStyle]} />;
26
+ },
27
+ });
28
+
29
+ const Day = ({
30
+ day,
31
+ onSelectDate,
32
+ containerHeight = CONTAINER_HEIGHT,
33
+ weekdaysHeight = WEEKDAYS_HEIGHT,
34
+ }: Props) => {
35
+ const {
36
+ text,
37
+ date,
38
+ isDisabled,
39
+ isCurrentMonth,
40
+ isToday,
41
+ isSelected,
42
+ inRange,
43
+ leftCrop,
44
+ rightCrop,
45
+ isStartOfWeek,
46
+ isEndOfWeek,
47
+ isCrop,
48
+ inMiddle,
49
+ rangeStart,
50
+ rangeEnd,
51
+ } = day;
52
+
53
+ styles.useVariants({
54
+ isToday,
55
+ isSelected,
56
+ isDisabled,
57
+ isCurrentMonth,
58
+ inMiddle,
59
+ rangeStart,
60
+ rangeEnd,
61
+ isStartOfWeek,
62
+ isEndOfWeek,
63
+ });
64
+
65
+ const RangeFill = useMemo(() => {
66
+ if (!inRange) return null;
67
+ if (!isCrop) {
68
+ return <View style={[styles.rangeRoot]} />;
69
+ }
70
+ return (
71
+ <>
72
+ {leftCrop && <View style={[styles.rangeRoot, styles.leftCrop]} />}
73
+ {rightCrop && <View style={[styles.rangeRoot, styles.rightCrop]} />}
74
+ </>
75
+ );
76
+ }, [inRange, isCrop, leftCrop, rightCrop, styles.rangeRoot]);
77
+
78
+ const accessibilityLabel = useMemo(() => {
79
+ const dayDate = new Date(date);
80
+ const dayOfWeek = dayDate.toLocaleDateString('en-US', { weekday: 'long' });
81
+ const dayOfMonth = dayDate.toLocaleDateString('en-US', { day: 'numeric' });
82
+ const month = dayDate.toLocaleDateString('en-US', { month: 'long' });
83
+ return `${dayOfWeek}, ${month} ${dayOfMonth}${isSelected ? ', selected' : ''}`;
84
+ }, [date]);
85
+
86
+ return (
87
+ <View style={styles.dayWrapper}>
88
+ <View style={[styles.dayCell(containerHeight, weekdaysHeight)]}>
89
+ {RangeFill}
90
+ <DayPressable
91
+ disabled={isDisabled}
92
+ onPress={() => onSelectDate(date)}
93
+ accessibilityRole="button"
94
+ accessibilityLabel={accessibilityLabel}
95
+ style={styles.dayContainer}
96
+ >
97
+ <BodyText style={[styles.text]}>{text}</BodyText>
98
+ </DayPressable>
99
+ <View style={[styles.indicator]} />
100
+ </View>
101
+ </View>
102
+ );
103
+ };
104
+
105
+ const styles = StyleSheet.create(theme => ({
106
+ dayWrapper: {
107
+ width: `${99.9 / 7}%`,
108
+ padding: 2,
109
+ variants: {
110
+ isDisabled: {
111
+ true: {
112
+ opacity: theme.opacity.disabled,
113
+ },
114
+ },
115
+ isCurrentMonth: {
116
+ false: {
117
+ opacity: theme.opacity.disabled,
118
+ },
119
+ },
120
+ },
121
+ compoundVariants: [
122
+ {
123
+ isCurrentMonth: false,
124
+ inMiddle: true,
125
+ styles: {
126
+ opacity: 1,
127
+ },
128
+ },
129
+ {
130
+ isCurrentMonth: false,
131
+ rangeStart: true,
132
+ styles: {
133
+ opacity: 1,
134
+ },
135
+ },
136
+ {
137
+ isCurrentMonth: false,
138
+ rangeEnd: true,
139
+ styles: {
140
+ opacity: 1,
141
+ },
142
+ },
143
+ ],
144
+ },
145
+ dayContainer: {
146
+ flex: 1,
147
+ width: '100%',
148
+ justifyContent: 'center',
149
+ alignItems: 'center',
150
+ borderRadius: theme.components.datePicker.calendar.item.borderRadius,
151
+ variants: {
152
+ isSelected: {
153
+ true: {
154
+ backgroundColor: theme.color.surface.brand.default,
155
+ },
156
+ },
157
+ rangeStart: {
158
+ true: {
159
+ backgroundColor: theme.color.surface.brand.default,
160
+ },
161
+ },
162
+ inMiddle: {
163
+ true: {
164
+ _web: {
165
+ _hover: {
166
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
167
+ },
168
+ },
169
+ },
170
+ },
171
+ rangeEnd: {
172
+ true: {
173
+ backgroundColor: theme.color.surface.brand.default,
174
+ },
175
+ },
176
+ isActive: {
177
+ true: {
178
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
179
+ },
180
+ },
181
+ },
182
+ compoundVariants: [
183
+ {
184
+ isActive: true,
185
+ inMiddle: true,
186
+ styles: {
187
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
188
+ },
189
+ },
190
+ ],
191
+ _web: {
192
+ '_focus-visible': {
193
+ ...theme.helpers.focusVisible,
194
+ },
195
+ _active: {
196
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
197
+ },
198
+ },
199
+ },
200
+ text: {
201
+ color: theme.color.text.primary,
202
+ variants: {
203
+ isSelected: {
204
+ true: {
205
+ color: theme.color.text.inverted,
206
+ },
207
+ },
208
+ },
209
+ },
210
+ indicator: {
211
+ position: 'absolute',
212
+ width: theme.components.datePicker.calendar.item.roundelWidth,
213
+ height: theme.components.datePicker.calendar.item.roundelHeight,
214
+ top: 34,
215
+ left: '50%',
216
+ transform: [{ translateX: -(theme.components.datePicker.calendar.item.roundelWidth / 2) }],
217
+ borderRadius: theme.borderRadius.full,
218
+ variants: {
219
+ isToday: {
220
+ true: {
221
+ backgroundColor: theme.color.surface.brand.default,
222
+ },
223
+ },
224
+ isSelected: {
225
+ true: {},
226
+ },
227
+ },
228
+ compoundVariants: [
229
+ {
230
+ isToday: true,
231
+ isSelected: true,
232
+ styles: {
233
+ backgroundColor: theme.components.datePicker.calendar.item.roundelBackgroundColorInverted,
234
+ },
235
+ },
236
+ ],
237
+ },
238
+ dayCell: (containerHeight: number, weekdaysHeight: number) => ({
239
+ minHeight: (containerHeight - weekdaysHeight) / 6,
240
+ borderRadius: theme.components.datePicker.calendar.item.borderRadius,
241
+ justifyContent: 'center',
242
+ alignItems: 'center',
243
+ backgroundColor: theme.color.background.secondary,
244
+ }),
245
+ rangeWrapper: {
246
+ flex: 1,
247
+ },
248
+ rangeRoot: {
249
+ position: 'absolute',
250
+ top: 0,
251
+ left: -2,
252
+ right: -2,
253
+ bottom: 0,
254
+ variants: {
255
+ inMiddle: {
256
+ true: {
257
+ backgroundColor: theme.components.datePicker.calendar.item.rangeBackground,
258
+ _web: {
259
+ _hover: {
260
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
261
+ },
262
+ },
263
+ },
264
+ },
265
+ isStartOfWeek: {
266
+ true: {},
267
+ },
268
+ isEndOfWeek: {
269
+ true: {},
270
+ },
271
+ },
272
+ },
273
+ leftCrop: {
274
+ left: '50%',
275
+ backgroundColor: theme.components.datePicker.calendar.item.rangeBackground,
276
+ _web: {
277
+ _hover: {
278
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
279
+ },
280
+ },
281
+ },
282
+ rightCrop: {
283
+ right: '50%',
284
+ backgroundColor: theme.components.datePicker.calendar.item.rangeBackground,
285
+ _web: {
286
+ _hover: {
287
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
288
+ },
289
+ },
290
+ },
291
+ }));
292
+
293
+ const customComparator = (prev: Readonly<Props>, next: Readonly<Props>) => {
294
+ const areEqual =
295
+ isEqual(prev.day, next.day) &&
296
+ prev.onSelectDate === next.onSelectDate &&
297
+ prev.containerHeight === next.containerHeight;
298
+
299
+ return areEqual;
300
+ };
301
+
302
+ export default memo(Day, customComparator);
@@ -0,0 +1,241 @@
1
+ import dayjs from 'dayjs';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { View } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { useDatePickerContext } from './DatePicker.context';
6
+ import { DateType } from './DatePicker.props';
7
+ import Day, { EmptyDay } from './DatePickerDay';
8
+ import Weekdays from './DatePickerWeekdays';
9
+ import {
10
+ areDatesOnSameDay,
11
+ getDate,
12
+ getDaysInMonth,
13
+ getMonthDays,
14
+ getParsedDate,
15
+ isDateBetween,
16
+ } from './utils';
17
+
18
+ const Days = () => {
19
+ const {
20
+ mode,
21
+ numerals = 'latn',
22
+ timeZone,
23
+ date,
24
+ startDate,
25
+ endDate,
26
+ dates,
27
+ currentDate,
28
+ onSelectDate,
29
+ showOutsideDays,
30
+ minDate,
31
+ maxDate,
32
+ enabledDates,
33
+ disabledDates,
34
+ firstDayOfWeek,
35
+ containerHeight,
36
+ weekdaysHeight,
37
+ weekdaysFormat,
38
+ multiRangeMode,
39
+ hideWeekdays,
40
+ } = useDatePickerContext();
41
+
42
+ const { year, month, hour, minute } = getParsedDate(currentDate);
43
+
44
+ const handleSelectDate = useCallback(
45
+ (selectedDate: DateType) => {
46
+ const newDate = getDate(selectedDate).hour(hour).minute(minute);
47
+
48
+ onSelectDate(newDate);
49
+ },
50
+ [onSelectDate, hour, minute]
51
+ );
52
+
53
+ const daysGrid = useMemo(() => {
54
+ const today = dayjs().tz(timeZone);
55
+ dayjs.tz.setDefault(timeZone);
56
+
57
+ const { fullDaysInMonth, prevMonthDays, prevMonthOffset, daysInCurrentMonth, daysInNextMonth } =
58
+ getDaysInMonth(currentDate, showOutsideDays, firstDayOfWeek);
59
+
60
+ return getMonthDays(
61
+ currentDate,
62
+ showOutsideDays,
63
+ minDate,
64
+ maxDate,
65
+ firstDayOfWeek,
66
+ enabledDates,
67
+ disabledDates,
68
+ prevMonthDays,
69
+ prevMonthOffset,
70
+ daysInCurrentMonth,
71
+ daysInNextMonth,
72
+ numerals
73
+ ).map((day, index) => {
74
+ if (!day) return null;
75
+
76
+ let leftCrop = day.dayOfMonth === 1;
77
+ let rightCrop = day.dayOfMonth === fullDaysInMonth;
78
+ const isFirstDayOfMonth = day.dayOfMonth === 1;
79
+ const isLastDayOfMonth = day.dayOfMonth === fullDaysInMonth;
80
+ const isToday = areDatesOnSameDay(day.date, today);
81
+ let inRange = false;
82
+ let isSelected = false;
83
+ let isCrop = false;
84
+ let inMiddle = false;
85
+ let rangeStart = false;
86
+ let rangeEnd = false;
87
+
88
+ if (mode === 'range') {
89
+ rightCrop = false;
90
+ const selectedStartDay = areDatesOnSameDay(day.date, startDate);
91
+ const selectedEndDay = areDatesOnSameDay(day.date, endDate);
92
+ isSelected = selectedStartDay || selectedEndDay;
93
+ inRange = isDateBetween(day.date, { startDate, endDate });
94
+
95
+ if (selectedStartDay) leftCrop = true;
96
+ if (selectedEndDay) rightCrop = true;
97
+ if (index % 7 === 0 && !selectedStartDay) leftCrop = false;
98
+ if (index % 7 === 6 && !selectedEndDay) rightCrop = false;
99
+
100
+ if (
101
+ (isFirstDayOfMonth && selectedEndDay) ||
102
+ (isLastDayOfMonth && selectedStartDay) ||
103
+ dayjs(startDate).format('DDMMYYYY') === dayjs(endDate).format('DDMMYYYY')
104
+ ) {
105
+ inRange = false;
106
+ }
107
+
108
+ isCrop = inRange && (leftCrop || rightCrop) && !(leftCrop && rightCrop);
109
+ inMiddle = inRange && !leftCrop && !rightCrop;
110
+ rangeStart = inRange && leftCrop;
111
+ rangeEnd = inRange && rightCrop;
112
+ } else if (mode === 'multiple') {
113
+ const safeDates = dates || [];
114
+ isSelected = safeDates.some(d => areDatesOnSameDay(day.date, d));
115
+
116
+ // if the selected days in a row, implements range mode style to selected days
117
+ if (multiRangeMode) {
118
+ const yesterday = dayjs(day.date).subtract(1, 'day');
119
+ const tomorrow = dayjs(day.date).add(1, 'day');
120
+
121
+ const yesterdaySelected = safeDates.some(d => areDatesOnSameDay(d, yesterday));
122
+ const tomorrowSelected = safeDates.some(d => areDatesOnSameDay(d, tomorrow));
123
+
124
+ // Reset all flags
125
+ inRange = false;
126
+ leftCrop = false;
127
+ rightCrop = false;
128
+
129
+ if (isSelected) {
130
+ // Case: both adjacent days are selected - this is a middle day
131
+ if (yesterdaySelected && tomorrowSelected) {
132
+ inRange = true;
133
+ }
134
+ // Case: only tomorrow is selected - this is the start of a range
135
+ else if (tomorrowSelected) {
136
+ inRange = true;
137
+ leftCrop = true;
138
+ }
139
+ // Case: only yesterday is selected - this is the end of a range
140
+ else if (yesterdaySelected) {
141
+ inRange = true;
142
+ rightCrop = true;
143
+ }
144
+
145
+ // Handle edge cases for first and last days of month
146
+ // Only apply these special cases when the day is actually part of a range
147
+ if (inRange) {
148
+ if (isFirstDayOfMonth && !tomorrowSelected) {
149
+ inRange = false;
150
+ }
151
+ if (isLastDayOfMonth && !yesterdaySelected) {
152
+ inRange = false;
153
+ }
154
+ }
155
+ }
156
+ // Set derived flags based on the core flags
157
+ isCrop = inRange && (leftCrop || rightCrop);
158
+ inMiddle = inRange && !leftCrop && !rightCrop;
159
+ rangeStart = inRange && leftCrop;
160
+ rangeEnd = inRange && rightCrop;
161
+ }
162
+ } else if (mode === 'single') {
163
+ isSelected = areDatesOnSameDay(day.date, date);
164
+ }
165
+
166
+ return {
167
+ ...day,
168
+ isToday,
169
+ isSelected,
170
+ inRange,
171
+ leftCrop,
172
+ rightCrop,
173
+ isCrop,
174
+ inMiddle,
175
+ rangeStart,
176
+ rangeEnd,
177
+ };
178
+ });
179
+ }, [
180
+ mode,
181
+ numerals,
182
+ timeZone,
183
+ month,
184
+ year,
185
+ showOutsideDays,
186
+ firstDayOfWeek,
187
+ minDate,
188
+ maxDate,
189
+ enabledDates,
190
+ disabledDates,
191
+ date,
192
+ startDate,
193
+ endDate,
194
+ dates,
195
+ multiRangeMode,
196
+ currentDate,
197
+ ]);
198
+
199
+ return (
200
+ <View style={styles.container} testID="day-selector">
201
+ {!hideWeekdays ? (
202
+ <Weekdays
203
+ firstDayOfWeek={firstDayOfWeek}
204
+ weekdaysFormat={weekdaysFormat}
205
+ weekdaysHeight={weekdaysHeight}
206
+ />
207
+ ) : null}
208
+ <View style={styles.daysContainer} testID="days">
209
+ {daysGrid?.map((day, index) => {
210
+ return day ? (
211
+ <Day
212
+ key={index}
213
+ day={day}
214
+ onSelectDate={handleSelectDate}
215
+ containerHeight={containerHeight}
216
+ weekdaysHeight={weekdaysHeight}
217
+ />
218
+ ) : (
219
+ <EmptyDay key={index} />
220
+ );
221
+ })}
222
+ </View>
223
+ </View>
224
+ );
225
+ };
226
+
227
+ const styles = StyleSheet.create({
228
+ container: {
229
+ width: '100%',
230
+ height: '100%',
231
+ },
232
+ daysContainer: {
233
+ width: '100%',
234
+ height: '100%',
235
+ flexWrap: 'wrap',
236
+ flexDirection: 'row',
237
+ alignContent: 'flex-start',
238
+ },
239
+ });
240
+
241
+ export default Days;
@@ -0,0 +1,35 @@
1
+ import { View } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { Button } from '../Button';
4
+ import { useDatePickerContext } from './DatePicker.context';
5
+
6
+ const Footer = () => {
7
+ const { closeDatePicker, onCancel } = useDatePickerContext();
8
+
9
+ const handleCancel = () => {
10
+ if (onCancel) {
11
+ onCancel();
12
+ }
13
+ closeDatePicker();
14
+ };
15
+ return (
16
+ <View style={styles.container} testID="footer">
17
+ <Button variant="ghost" colorScheme="functional" onPress={handleCancel}>
18
+ Cancel
19
+ </Button>
20
+ <Button variant="ghost" colorScheme="functional" onPress={closeDatePicker}>
21
+ Ok
22
+ </Button>
23
+ </View>
24
+ );
25
+ };
26
+
27
+ const styles = StyleSheet.create(theme => ({
28
+ container: {
29
+ flexDirection: 'row',
30
+ gap: theme.components.datePicker.calendar.footer.gap,
31
+ justifyContent: 'flex-end',
32
+ },
33
+ }));
34
+
35
+ export default Footer;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { NavigationPosition } from '../DatePicker.props';
3
+
4
+ export type HeaderProps = {
5
+ PrevIcon?: React.ReactNode;
6
+ NextIcon?: React.ReactNode;
7
+ navigationPosition?: NavigationPosition;
8
+ };
9
+
10
+ export type NavigationProps = {};
@@ -0,0 +1,29 @@
1
+ import dayjs from 'dayjs';
2
+ import { memo } from 'react';
3
+ import { Pressable } from 'react-native';
4
+ import { BodyText } from '../../BodyText';
5
+ import { useDatePickerContext } from '../DatePicker.context';
6
+
7
+ const MonthButton = () => {
8
+ const { currentDate, calendarView, setCalendarView, disableMonthPicker, monthCaptionFormat } =
9
+ useDatePickerContext();
10
+
11
+ const currentMonthText = dayjs(currentDate)
12
+ .locale('en')
13
+ .format(monthCaptionFormat === 'full' ? 'MMMM' : 'MMM');
14
+
15
+ return (
16
+ <Pressable
17
+ disabled={disableMonthPicker}
18
+ onPress={() => setCalendarView(calendarView === 'month' ? 'day' : 'month')}
19
+ testID="btn-month"
20
+ accessibilityRole="button"
21
+ accessibilityLabel={currentMonthText}
22
+ accessibilityHint={`${calendarView === 'month' ? 'Tap to switch to day view' : 'Tap to switch to month view'}`}
23
+ >
24
+ <BodyText weight="semibold">{currentMonthText}</BodyText>
25
+ </Pressable>
26
+ );
27
+ };
28
+
29
+ export default memo(MonthButton);
@@ -0,0 +1,46 @@
1
+ import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
2
+ import { memo, useCallback } from 'react';
3
+ import { Pressable } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { Icon } from '../../Icon';
6
+ import { useDatePickerContext } from '../DatePicker.context';
7
+ import { YEAR_PAGE_SIZE } from '../utils';
8
+
9
+ const NextButton = () => {
10
+ const { currentYear, onChangeMonth, onChangeYear, calendarView } = useDatePickerContext();
11
+
12
+ const onPress = useCallback(() => {
13
+ switch (calendarView) {
14
+ case 'day':
15
+ return onChangeMonth(1);
16
+ case 'month':
17
+ return onChangeYear(currentYear + 1);
18
+ case 'year':
19
+ return onChangeYear(currentYear + YEAR_PAGE_SIZE);
20
+ default:
21
+ return {};
22
+ }
23
+ }, [calendarView, currentYear, onChangeMonth, onChangeYear]);
24
+
25
+ return (
26
+ <Pressable
27
+ disabled={calendarView === 'time'}
28
+ onPress={onPress}
29
+ testID="btn-next"
30
+ accessibilityRole="button"
31
+ accessibilityLabel={`Next ${calendarView === 'day' ? 'month' : calendarView === 'month' ? 'year' : 'years'}`}
32
+ >
33
+ <Icon as={ChevronRightSmallIcon} style={styles.icon} />
34
+ </Pressable>
35
+ );
36
+ };
37
+
38
+ const styles = StyleSheet.create(theme => ({
39
+ icon: {
40
+ width: 20,
41
+ height: 20,
42
+ color: theme.color.icon.primary,
43
+ },
44
+ }));
45
+
46
+ export default memo(NextButton);
@@ -0,0 +1,46 @@
1
+ import { ChevronLeftSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
2
+ import { memo, useCallback } from 'react';
3
+ import { Pressable } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { Icon } from '../../Icon';
6
+ import { useDatePickerContext } from '../DatePicker.context';
7
+ import { YEAR_PAGE_SIZE } from '../utils';
8
+
9
+ const PrevButton = () => {
10
+ const { currentYear, calendarView, onChangeMonth, onChangeYear } = useDatePickerContext();
11
+
12
+ const onPress = useCallback(() => {
13
+ switch (calendarView) {
14
+ case 'day':
15
+ return onChangeMonth(-1);
16
+ case 'month':
17
+ return onChangeYear(currentYear - 1);
18
+ case 'year':
19
+ return onChangeYear(currentYear - YEAR_PAGE_SIZE);
20
+ default:
21
+ return {};
22
+ }
23
+ }, [calendarView, currentYear, onChangeMonth, onChangeYear]);
24
+
25
+ return (
26
+ <Pressable
27
+ disabled={calendarView === 'time'}
28
+ onPress={onPress}
29
+ testID="btn-prev"
30
+ accessibilityRole="button"
31
+ accessibilityLabel={`Prev ${calendarView === 'day' ? 'month' : calendarView === 'month' ? 'year' : 'years'}`}
32
+ >
33
+ <Icon as={ChevronLeftSmallIcon} style={styles.icon} />
34
+ </Pressable>
35
+ );
36
+ };
37
+
38
+ const styles = StyleSheet.create(theme => ({
39
+ icon: {
40
+ width: 20,
41
+ height: 20,
42
+ color: theme.color.icon.primary,
43
+ },
44
+ }));
45
+
46
+ export default memo(PrevButton);