@utilitywarehouse/hearth-react-native 0.3.1 → 0.4.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 (193) 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 +10 -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/hooks/index.d.ts +4 -3
  93. package/build/hooks/index.js +4 -3
  94. package/build/hooks/usePrevious.d.ts +1 -0
  95. package/build/hooks/usePrevious.js +8 -0
  96. package/build/hooks/useTheme.d.ts +2 -1
  97. package/build/hooks/useTheme.js +2 -2
  98. package/build/tokens/components/dark/date-picker.d.ts +1 -0
  99. package/build/tokens/components/dark/date-picker.js +1 -0
  100. package/build/tokens/components/dark/illustrations.d.ts +7 -0
  101. package/build/tokens/components/dark/illustrations.js +6 -0
  102. package/build/tokens/components/dark/index.d.ts +1 -0
  103. package/build/tokens/components/dark/index.js +1 -0
  104. package/build/tokens/components/dark/segmented-control.d.ts +2 -2
  105. package/build/tokens/components/dark/segmented-control.js +2 -2
  106. package/build/tokens/components/dark/table.d.ts +3 -0
  107. package/build/tokens/components/dark/table.js +3 -0
  108. package/build/tokens/components/light/date-picker.d.ts +1 -0
  109. package/build/tokens/components/light/date-picker.js +1 -0
  110. package/build/tokens/components/light/illustrations.d.ts +7 -0
  111. package/build/tokens/components/light/illustrations.js +6 -0
  112. package/build/tokens/components/light/index.d.ts +1 -0
  113. package/build/tokens/components/light/index.js +1 -0
  114. package/build/tokens/components/light/segmented-control.d.ts +2 -2
  115. package/build/tokens/components/light/segmented-control.js +2 -2
  116. package/build/tokens/components/light/table.d.ts +3 -0
  117. package/build/tokens/components/light/table.js +3 -0
  118. package/build/utils/index.d.ts +1 -0
  119. package/build/utils/index.js +1 -0
  120. package/build/utils/isEqual.d.ts +2 -0
  121. package/build/utils/isEqual.js +29 -0
  122. package/chromatic.config.json +6 -0
  123. package/docs/components/AllComponents.web.tsx +43 -1
  124. package/docs/components/ViewWrap.tsx +32 -0
  125. package/docs/components/index.ts +1 -0
  126. package/docs/getting-started.mdx +6 -6
  127. package/package.json +10 -7
  128. package/src/components/CurrencyInput/CurrencyInput.tsx +2 -1
  129. package/src/components/DatePicker/DatePicker.context.ts +23 -0
  130. package/src/components/DatePicker/DatePicker.docs.mdx +239 -0
  131. package/src/components/DatePicker/DatePicker.props.ts +139 -0
  132. package/src/components/DatePicker/DatePicker.stories.tsx +98 -0
  133. package/src/components/DatePicker/DatePicker.tsx +669 -0
  134. package/src/components/DatePicker/DatePickerCalendar.tsx +41 -0
  135. package/src/components/DatePicker/DatePickerDay.tsx +302 -0
  136. package/src/components/DatePicker/DatePickerDays.tsx +241 -0
  137. package/src/components/DatePicker/DatePickerFooter.tsx +35 -0
  138. package/src/components/DatePicker/DatePickerHeader/DatePickerHeader.props.ts +10 -0
  139. package/src/components/DatePicker/DatePickerHeader/DatePickerMonthButton.tsx +29 -0
  140. package/src/components/DatePicker/DatePickerHeader/DatePickerNextButton.tsx +46 -0
  141. package/src/components/DatePicker/DatePickerHeader/DatePickerPrevButton.tsx +46 -0
  142. package/src/components/DatePicker/DatePickerHeader/DatePickerSelectors.tsx +96 -0
  143. package/src/components/DatePicker/DatePickerHeader/DatePickerTimeButton.tsx +48 -0
  144. package/src/components/DatePicker/DatePickerHeader/DatePickerYearButton.tsx +50 -0
  145. package/src/components/DatePicker/DatePickerHeader/index.tsx +64 -0
  146. package/src/components/DatePicker/DatePickerMonths.tsx +101 -0
  147. package/src/components/DatePicker/DatePickerWeekdays.tsx +49 -0
  148. package/src/components/DatePicker/DatePickerYears.tsx +119 -0
  149. package/src/components/DatePicker/TimePicker.tsx +141 -0
  150. package/src/components/DatePicker/enums.ts +14 -0
  151. package/src/components/DatePicker/index.ts +13 -0
  152. package/src/components/DatePicker/polyfill.ts +21 -0
  153. package/src/components/DatePicker/time-picker/animated-math.ts +33 -0
  154. package/src/components/DatePicker/time-picker/period-native.tsx +34 -0
  155. package/src/components/DatePicker/time-picker/period-picker.tsx +16 -0
  156. package/src/components/DatePicker/time-picker/period-web.tsx +36 -0
  157. package/src/components/DatePicker/time-picker/wheel-native.tsx +37 -0
  158. package/src/components/DatePicker/time-picker/wheel-picker/index.ts +3 -0
  159. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker-item.tsx +132 -0
  160. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.style.ts +22 -0
  161. package/src/components/DatePicker/time-picker/wheel-picker/wheel-picker.tsx +200 -0
  162. package/src/components/DatePicker/time-picker/wheel-web.tsx +181 -0
  163. package/src/components/DatePicker/time-picker/wheel.tsx +18 -0
  164. package/src/components/DatePicker/utils.ts +549 -0
  165. package/src/components/DatePickerInput/DatePickerInput.docs.mdx +237 -0
  166. package/src/components/DatePickerInput/DatePickerInput.props.ts +50 -0
  167. package/src/components/DatePickerInput/DatePickerInput.stories.tsx +178 -0
  168. package/src/components/DatePickerInput/DatePickerInput.tsx +265 -0
  169. package/src/components/DatePickerInput/DatePickerInputDoneButton.tsx +42 -0
  170. package/src/components/DatePickerInput/DatePickerInputDoneButton.web.tsx +7 -0
  171. package/src/components/DatePickerInput/index.ts +2 -0
  172. package/src/components/Icon/Icon.stories.tsx +4 -3
  173. package/src/components/Input/InputField.tsx +0 -2
  174. package/src/components/Input/InputSlot.tsx +14 -3
  175. package/src/components/Modal/Modal.stories.tsx +2 -30
  176. package/src/components/UnstyledIconButton/UnstyledIconButton.tsx +14 -2
  177. package/src/components/UnstyledIconButton/UnstyledIconButtonRoot.tsx +7 -3
  178. package/src/components/index.ts +2 -0
  179. package/src/hooks/index.ts +4 -3
  180. package/src/hooks/usePrevious.ts +9 -0
  181. package/src/hooks/useTheme.ts +4 -3
  182. package/src/tokens/components/dark/date-picker.ts +1 -0
  183. package/src/tokens/components/dark/illustrations.ts +7 -0
  184. package/src/tokens/components/dark/index.ts +1 -0
  185. package/src/tokens/components/dark/segmented-control.ts +2 -2
  186. package/src/tokens/components/dark/table.ts +3 -0
  187. package/src/tokens/components/light/date-picker.ts +1 -0
  188. package/src/tokens/components/light/illustrations.ts +7 -0
  189. package/src/tokens/components/light/index.ts +1 -0
  190. package/src/tokens/components/light/segmented-control.ts +2 -2
  191. package/src/tokens/components/light/table.ts +3 -0
  192. package/src/utils/index.ts +1 -0
  193. package/src/utils/isEqual.ts +30 -0
@@ -0,0 +1,265 @@
1
+ import type { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
2
+ import { CalendarSmallIcon, CloseSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
3
+ import dayjs from 'dayjs';
4
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { Keyboard, Platform, TextInputFocusEvent } from 'react-native';
7
+ import { StyleSheet } from 'react-native-unistyles';
8
+ import { DatePicker } from '../DatePicker';
9
+ import type { DateType } from '../DatePicker/DatePicker.props';
10
+ import { useFormFieldContext } from '../FormField';
11
+ import { Input, InputField, InputSlot } from '../Input';
12
+ import { UnstyledIconButton } from '../UnstyledIconButton';
13
+ import type DatePickerInputProps from './DatePickerInput.props';
14
+ import DatePickerInputDoneButton from './DatePickerInputDoneButton';
15
+
16
+ dayjs.extend(customParseFormat);
17
+
18
+ const DEFAULT_FORMAT = 'DD/MM/YYYY';
19
+
20
+ const maskDefaultFormat = (value: string) => {
21
+ const digitsOnly = value.replace(/\D/g, '').slice(0, 8);
22
+ const day = digitsOnly.slice(0, 2);
23
+ const month = digitsOnly.slice(2, 4);
24
+ const year = digitsOnly.slice(4, 8);
25
+
26
+ return [day, month, year].filter(Boolean).join('/');
27
+ };
28
+
29
+ const DatePickerInput = ({
30
+ validationStatus = 'initial',
31
+ disabled,
32
+ focused,
33
+ readonly,
34
+ placeholder,
35
+ inBottomSheet = false,
36
+ format = DEFAULT_FORMAT,
37
+ openButtonLabel = 'Open date picker',
38
+ autoCloseOnSelect = true,
39
+ onChange,
40
+ onChangeText,
41
+ onBlur,
42
+ onFocus,
43
+ value,
44
+ datePickerProps,
45
+ onClear,
46
+ ...rest
47
+ }: DatePickerInputProps) => {
48
+ const formFieldContext = useFormFieldContext();
49
+ const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
50
+ const isDisabled = formFieldContext?.disabled ?? disabled;
51
+ const isReadonly = formFieldContext?.readonly ?? readonly;
52
+
53
+ const pickerRef = useRef<BottomSheetModalMethods | null>(null);
54
+ const accessoryViewID = useMemo(() => {
55
+ if (Platform.OS !== 'ios') return undefined;
56
+ return `datepicker-input-${Math.random().toString(36).slice(2)}`;
57
+ }, []);
58
+
59
+ const formatDate = useCallback(
60
+ (dateValue?: DateType) => {
61
+ if (!dateValue) return '';
62
+ const parsed = dayjs(dateValue);
63
+ return parsed.isValid() ? parsed.format(format) : '';
64
+ },
65
+ [format]
66
+ );
67
+
68
+ const isDefaultFormat = format === DEFAULT_FORMAT;
69
+
70
+ const [inputValue, setInputValue] = useState(() => formatDate(value));
71
+
72
+ useEffect(() => {
73
+ setInputValue(formatDate(value));
74
+ }, [value, formatDate]);
75
+
76
+ const handleTextChange = useCallback(
77
+ (text: string) => {
78
+ const nextValue = isDefaultFormat ? maskDefaultFormat(text) : text;
79
+
80
+ setInputValue(nextValue);
81
+ onChangeText?.(nextValue);
82
+
83
+ if (!nextValue) {
84
+ onChange?.({ date: undefined });
85
+ return;
86
+ }
87
+
88
+ const parsed = dayjs(nextValue, format, true);
89
+
90
+ if (parsed.isValid()) {
91
+ onChange?.({ date: parsed.toDate() });
92
+ } else {
93
+ onChange?.({ date: undefined });
94
+ }
95
+ },
96
+ [format, isDefaultFormat, onChange, onChangeText]
97
+ );
98
+
99
+ const handleClear = useCallback(() => {
100
+ setInputValue('');
101
+ onChange?.({ date: undefined });
102
+ onClear?.();
103
+ }, [onChange, onClear]);
104
+
105
+ const closeKeyboard = useCallback(() => {
106
+ Keyboard.dismiss();
107
+ }, []);
108
+
109
+ const openPicker = useCallback(() => {
110
+ if (isDisabled || isReadonly) return;
111
+ closeKeyboard();
112
+ pickerRef.current?.present();
113
+ }, [closeKeyboard, isDisabled, isReadonly]);
114
+
115
+ const selectedDate = useMemo(() => {
116
+ if (value) {
117
+ return value;
118
+ }
119
+
120
+ const parsed = dayjs(inputValue, format, true);
121
+ return parsed.isValid() ? parsed.toDate() : undefined;
122
+ }, [value, inputValue, format]);
123
+
124
+ const handlePickerChange = useCallback(
125
+ ({ date }: { date: DateType }) => {
126
+ if (!date) {
127
+ handleClear();
128
+ return;
129
+ }
130
+
131
+ const formatted = formatDate(date);
132
+ setInputValue(formatted);
133
+ onChange?.({ date });
134
+
135
+ if (autoCloseOnSelect) {
136
+ pickerRef.current?.close?.();
137
+ }
138
+ },
139
+ [autoCloseOnSelect, formatDate, handleClear, onChange]
140
+ );
141
+
142
+ const handleBlur = useCallback(
143
+ (event: TextInputFocusEvent) => {
144
+ onBlur?.(event);
145
+ },
146
+ [onBlur]
147
+ );
148
+
149
+ const handleFocus = useCallback(
150
+ (event: TextInputFocusEvent) => {
151
+ onFocus?.(event);
152
+ },
153
+ [onFocus]
154
+ );
155
+
156
+ const { onCancel: pickerOnCancel, ...restDatePickerProps } = datePickerProps ?? {};
157
+ const {
158
+ style: inputStyle,
159
+ keyboardType: keyboardTypeProp,
160
+ inputMode: inputModeProp,
161
+ accessibilityHint: accessibilityHintProp,
162
+ accessibilityLabel: accessibilityLabelProp,
163
+ accessible: accessibleProp,
164
+ accessibilityRole: accessibilityRoleProp,
165
+ importantForAccessibility: importantForAccessibilityProp,
166
+ ...textInputProps
167
+ } = rest;
168
+
169
+ const resolvedKeyboardType = keyboardTypeProp ?? (isDefaultFormat ? 'number-pad' : undefined);
170
+ const resolvedInputMode = inputModeProp ?? (isDefaultFormat ? 'numeric' : undefined);
171
+ const resolvedAccessibilityHint = accessibilityHintProp ?? `Enter the date in ${format} format`;
172
+ const resolvedAccessibilityLabel = accessibilityLabelProp ?? 'Date input';
173
+ const resolvedAccessible = accessibleProp ?? true;
174
+ const resolvedImportantForAccessibility = importantForAccessibilityProp ?? 'yes';
175
+
176
+ const handleCancel = useCallback(() => {
177
+ pickerOnCancel?.();
178
+ pickerRef.current?.close?.();
179
+ }, [pickerOnCancel]);
180
+
181
+ const placeholderValue = placeholder ?? format;
182
+
183
+ return (
184
+ <>
185
+ <Input
186
+ validationStatus={validationStatusFromContext}
187
+ disabled={isDisabled}
188
+ readonly={isReadonly}
189
+ focused={focused}
190
+ style={styles.wrap}
191
+ accessible={false}
192
+ >
193
+ <InputField
194
+ editable={!isReadonly && !isDisabled}
195
+ value={inputValue}
196
+ placeholder={placeholderValue}
197
+ onChangeText={handleTextChange}
198
+ onBlur={handleBlur}
199
+ onFocus={handleFocus}
200
+ inBottomSheet={inBottomSheet}
201
+ keyboardType={resolvedKeyboardType}
202
+ inputMode={resolvedInputMode}
203
+ accessibilityHint={resolvedAccessibilityHint}
204
+ aria-label="Date input"
205
+ accessibilityLabel={resolvedAccessibilityLabel}
206
+ accessible={resolvedAccessible}
207
+ accessibilityState={{
208
+ disabled: isDisabled || isReadonly,
209
+ }}
210
+ importantForAccessibility={resolvedImportantForAccessibility}
211
+ inputAccessoryViewID={Platform.OS === 'ios' ? accessoryViewID : undefined}
212
+ {...textInputProps}
213
+ style={[styles.input, inputStyle]}
214
+ />
215
+ {!!inputValue && onClear && !isReadonly && !isDisabled && (
216
+ <InputSlot accessibilityElementsHidden={false}>
217
+ <UnstyledIconButton
218
+ accessibilityLabel="Clear date"
219
+ accessibilityHint="Removes the current date"
220
+ icon={CloseSmallIcon}
221
+ onPress={handleClear}
222
+ />
223
+ </InputSlot>
224
+ )}
225
+ <InputSlot accessibilityElementsHidden={false}>
226
+ <UnstyledIconButton
227
+ accessibilityLabel={openButtonLabel}
228
+ accessibilityHint="Opens the date picker calendar"
229
+ icon={CalendarSmallIcon}
230
+ onPress={openPicker}
231
+ disabled={isDisabled || isReadonly}
232
+ />
233
+ </InputSlot>
234
+ </Input>
235
+ <DatePicker
236
+ ref={pickerRef}
237
+ mode="single"
238
+ date={selectedDate}
239
+ onChange={handlePickerChange}
240
+ onCancel={handleCancel}
241
+ {...restDatePickerProps}
242
+ />
243
+ {Platform.OS === 'ios' && accessoryViewID && (
244
+ <DatePickerInputDoneButton
245
+ accessoryViewID={accessoryViewID}
246
+ closeKeyboard={closeKeyboard}
247
+ />
248
+ )}
249
+ </>
250
+ );
251
+ };
252
+
253
+ DatePickerInput.displayName = 'DatePickerInput';
254
+
255
+ const styles = StyleSheet.create(theme => ({
256
+ wrap: {
257
+ gap: theme.components.input.date.gap,
258
+ },
259
+ input: {
260
+ paddingLeft: 0,
261
+ paddingRight: 0,
262
+ },
263
+ }));
264
+
265
+ export default DatePickerInput;
@@ -0,0 +1,42 @@
1
+ import { InputAccessoryView, View } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { Button } from '../Button';
4
+
5
+ const DatePickerInputDoneButton = ({
6
+ accessoryViewID,
7
+ closeKeyboard,
8
+ }: {
9
+ accessoryViewID: string;
10
+ closeKeyboard: () => void;
11
+ }) => {
12
+ return (
13
+ <InputAccessoryView nativeID={accessoryViewID}>
14
+ <View style={styles.accessory}>
15
+ <Button
16
+ accessibilityRole="button"
17
+ accessibilityLabel="Done"
18
+ onPress={closeKeyboard}
19
+ variant="ghost"
20
+ colorScheme="functional"
21
+ >
22
+ Done
23
+ </Button>
24
+ </View>
25
+ </InputAccessoryView>
26
+ );
27
+ };
28
+
29
+ const styles = StyleSheet.create(theme => ({
30
+ accessory: {
31
+ paddingHorizontal: 16,
32
+ paddingVertical: 2,
33
+ alignItems: 'flex-end',
34
+ backgroundColor: theme.color.surface.neutral.strong,
35
+ borderTopWidth: theme.borderWidth[1],
36
+ borderTopColor: theme.color.border.subtle,
37
+ },
38
+ }));
39
+
40
+ DatePickerInputDoneButton.DisplayName = 'DatePickerInputDoneButton';
41
+
42
+ export default DatePickerInputDoneButton;
@@ -0,0 +1,7 @@
1
+ const DatePickerInputDoneButton = () => {
2
+ return null;
3
+ };
4
+
5
+ DatePickerInputDoneButton.DisplayName = 'DatePickerInputDoneButton';
6
+
7
+ export default DatePickerInputDoneButton;
@@ -0,0 +1,2 @@
1
+ export { default as DatePickerInput } from './DatePickerInput';
2
+ export type { default as DatePickerInputProps } from './DatePickerInput.props';
@@ -1,6 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import * as Icons from '@utilitywarehouse/hearth-react-native-icons';
3
- import { ComponentType } from 'react';
4
3
  import { Icon } from '.';
5
4
  import { ColorValue } from '../../types';
6
5
  import { coloursAsArray } from '../../utils';
@@ -25,7 +24,8 @@ const meta = {
25
24
  },
26
25
  },
27
26
  args: {
28
- as: Object.keys(Icons)[0] as ComponentType,
27
+ // @ts-expect-error - This is a playground
28
+ as: Object.keys(Icons)[0],
29
29
  color: 'grey1000' as ColorValue,
30
30
  },
31
31
  } satisfies Meta<typeof Icon>;
@@ -37,7 +37,8 @@ type Story = StoryObj<typeof meta>;
37
37
  export const Playground: Story = {
38
38
  // @ts-expect-error - This is a playground
39
39
  render: ({ as: icon, color }) => {
40
+ // @ts-expect-error - This is a playground
40
41
  const as = icon === 'none' ? undefined : Icons[icon];
41
- return <Icon as={as} color={colors[color]} />;
42
+ return <Icon as={as} color={color} />;
42
43
  },
43
44
  };
@@ -6,8 +6,6 @@ import { useInputContext } from './Input.context';
6
6
 
7
7
  const InputField = ({
8
8
  style,
9
- onFocus,
10
- onBlur,
11
9
  inBottomSheet = false,
12
10
  ...props
13
11
  }: TextInputProps & { inBottomSheet?: boolean }) => {
@@ -1,9 +1,20 @@
1
- import { StyleSheet } from 'react-native-unistyles';
2
1
  import { View, ViewProps } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
3
 
4
- const InputSlot = ({ children, style, ...props }: ViewProps) => {
4
+ const InputSlot = ({
5
+ children,
6
+ style,
7
+ accessible,
8
+ importantForAccessibility,
9
+ ...props
10
+ }: ViewProps) => {
5
11
  return (
6
- <View {...props} style={[styles.container, style]}>
12
+ <View
13
+ {...props}
14
+ accessible={accessible ?? false}
15
+ importantForAccessibility={importantForAccessibility ?? 'auto'}
16
+ style={[styles.container, style]}
17
+ >
7
18
  {children}
8
19
  </View>
9
20
  );
@@ -1,9 +1,9 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useRef } from 'react';
3
- import { Dimensions, ImageSourcePropType, Platform, View, ViewProps } from 'react-native';
4
- import { UnistylesRuntime } from 'react-native-unistyles';
3
+ import { ImageSourcePropType, Platform, View } from 'react-native';
5
4
  import { Modal } from '.';
6
5
  import pigs from '../../../docs/assets/pigs.png';
6
+ import { ViewWrap } from '../../../docs/components';
7
7
  import { BodyText } from '../BodyText';
8
8
  import { BottomSheetModal } from '../BottomSheet';
9
9
  import { Box } from '../Box';
@@ -61,34 +61,6 @@ const meta = {
61
61
  export default meta;
62
62
  type Story = StoryObj<typeof meta>;
63
63
 
64
- const ViewWrap = ({ children }: { children: ViewProps['children'] }) => (
65
- <View
66
- style={{
67
- width: Platform.OS === 'web' ? '100%' : Dimensions.get('window').width,
68
- height:
69
- Platform.OS === 'web'
70
- ? '100%'
71
- : Dimensions.get('window').height -
72
- UnistylesRuntime.insets.top -
73
- UnistylesRuntime.insets.bottom -
74
- 33,
75
-
76
- position: 'absolute',
77
- top: 0,
78
- left: 0,
79
- right: 0,
80
- bottom: 0,
81
- flex: 1,
82
- marginVertical: Platform.OS === 'web' ? 0 : -8,
83
- justifyContent: 'center',
84
- alignItems: 'center',
85
- padding: 16,
86
- }}
87
- >
88
- {children}
89
- </View>
90
- );
91
-
92
64
  export const Playground: Story = {
93
65
  args: {
94
66
  children: [],
@@ -30,17 +30,29 @@ const UnstyledIconButton = ({
30
30
  ...props
31
31
  }: UnstyledIconButtonProps) => {
32
32
  const { disabled: groupDisabled, loading: groupLoading } = useButtonGroupContext();
33
- const { loading } = props;
33
+ const {
34
+ loading,
35
+ accessibilityRole = 'button',
36
+ accessible = true,
37
+ focusable = true,
38
+ importantForAccessibility = 'yes',
39
+ ...restProps
40
+ } = props;
41
+
34
42
  const isLoading = loading ?? groupLoading;
35
43
  const buttonDisabled = isLoading || (disabled ?? groupDisabled);
36
44
 
37
45
  return (
38
46
  <UnstyledIconButtonComponent
39
- {...props}
47
+ {...restProps}
40
48
  size={size}
41
49
  inverted={inverted}
42
50
  isDisabled={buttonDisabled}
43
51
  isPressed={pressed}
52
+ accessibilityRole={accessibilityRole}
53
+ accessible={accessible}
54
+ focusable={focusable}
55
+ importantForAccessibility={importantForAccessibility}
44
56
  icon={icon}
45
57
  >
46
58
  {loading ? (
@@ -1,8 +1,8 @@
1
- import React, { useMemo } from 'react';
2
- import type { UnstyledIconButtonProps } from './UnstyledIconButton.props';
1
+ import { useMemo } from 'react';
3
2
  import { Pressable, ViewStyle } from 'react-native';
4
3
  import { StyleSheet } from 'react-native-unistyles';
5
4
  import { UnstyledIconButtonContext } from './UnstyledIconButton.context';
5
+ import type { UnstyledIconButtonProps } from './UnstyledIconButton.props';
6
6
 
7
7
  const UnstyledIconButtonRoot = ({
8
8
  size,
@@ -18,7 +18,11 @@ const UnstyledIconButtonRoot = ({
18
18
  );
19
19
  return (
20
20
  <UnstyledIconButtonContext.Provider value={value}>
21
- <Pressable {...props} style={[styles.container, props.style as ViewStyle]} />
21
+ <Pressable
22
+ {...props}
23
+ accessibilityState={{ disabled: !!disabled, ...props.accessibilityState }}
24
+ style={[styles.container, props.style as ViewStyle]}
25
+ />
22
26
  </UnstyledIconButtonContext.Provider>
23
27
  );
24
28
  };
@@ -10,6 +10,8 @@ export * from './Card';
10
10
  export * from './Center';
11
11
  export * from './Checkbox';
12
12
  export * from './CurrencyInput';
13
+ export * from './DatePicker';
14
+ export * from './DatePickerInput';
13
15
  export * from './DescriptionList';
14
16
  export * from './DetailText';
15
17
  export * from './Divider';
@@ -1,6 +1,7 @@
1
- export { default as useColorMode } from './useColorMode';
2
1
  export { default as useBreakpointValue } from './useBreakpointValue';
3
- export { default as useTheme } from './useTheme';
2
+ export { default as useColorMode } from './useColorMode';
4
3
  export { default as useMedia } from './useMedia';
5
- export { default as useToken } from './useToken';
4
+ export { usePrevious } from './usePrevious';
6
5
  export { useStyleProps } from './useStyleProps';
6
+ export { default as useTheme } from './useTheme';
7
+ export { default as useToken } from './useToken';
@@ -0,0 +1,9 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ export const usePrevious = (value: any) => {
4
+ const ref = useRef(null);
5
+ useEffect(() => {
6
+ ref.current = value;
7
+ });
8
+ return ref.current;
9
+ };
@@ -1,8 +1,9 @@
1
- import { UnistylesRuntime } from 'react-native-unistyles';
1
+ import { useUnistyles } from 'react-native-unistyles';
2
+ import { AppThemes } from '../types';
2
3
 
3
4
  const useTheme = () => {
4
- const theme = UnistylesRuntime.getTheme();
5
- return theme;
5
+ const { theme } = useUnistyles();
6
+ return theme as AppThemes['light'] | AppThemes['dark'];
6
7
  };
7
8
 
8
9
  export default useTheme;
@@ -21,6 +21,7 @@ export default {
21
21
  item: {
22
22
  borderRadius: 8,
23
23
  minWidth: 32,
24
+ rangeBackground: '#191919',
24
25
  roundelBackgroundColorInverted: '#fcfbf2',
25
26
  roundelHeight: 6,
26
27
  roundelWidth: 6,
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ export default {
6
+ mode: 'dark',
7
+ } as const;
@@ -22,6 +22,7 @@ export { default as expandableCard } from './expandable-card';
22
22
  export { default as formField } from './form-field';
23
23
  export { default as iconButton } from './icon-button';
24
24
  export { default as iconContainer } from './icon-container';
25
+ export { default as illustrations } from './illustrations';
25
26
  export { default as indicatorIconButton } from './indicator-icon-button';
26
27
  export { default as inlineLink } from './inline-link';
27
28
  export { default as input } from './input';
@@ -3,10 +3,10 @@
3
3
  */
4
4
 
5
5
  export default {
6
- borderRadius: 6,
6
+ borderRadius: 9999,
7
7
  gap: 6,
8
8
  group: {
9
- borderRadius: 8,
9
+ borderRadius: 9999,
10
10
  borderWidth: 1,
11
11
  gap: 4,
12
12
  height: 48,
@@ -14,6 +14,9 @@ export default {
14
14
  headerCell: {
15
15
  borderWidth: 2,
16
16
  gap: 8,
17
+ neutral: {
18
+ backgroundColor: '#30302c',
19
+ },
17
20
  paddingHorizontal: 12,
18
21
  paddingVertical: 16,
19
22
  },
@@ -21,6 +21,7 @@ export default {
21
21
  item: {
22
22
  borderRadius: 8,
23
23
  minWidth: 32,
24
+ rangeBackground: '#f7f7f7',
24
25
  roundelBackgroundColorInverted: '#fcfbf2',
25
26
  roundelHeight: 6,
26
27
  roundelWidth: 6,
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ export default {
6
+ mode: 'light',
7
+ } as const;
@@ -22,6 +22,7 @@ export { default as expandableCard } from './expandable-card';
22
22
  export { default as formField } from './form-field';
23
23
  export { default as iconButton } from './icon-button';
24
24
  export { default as iconContainer } from './icon-container';
25
+ export { default as illustrations } from './illustrations';
25
26
  export { default as indicatorIconButton } from './indicator-icon-button';
26
27
  export { default as inlineLink } from './inline-link';
27
28
  export { default as input } from './input';
@@ -3,10 +3,10 @@
3
3
  */
4
4
 
5
5
  export default {
6
- borderRadius: 6,
6
+ borderRadius: 9999,
7
7
  gap: 6,
8
8
  group: {
9
- borderRadius: 8,
9
+ borderRadius: 9999,
10
10
  borderWidth: 1,
11
11
  gap: 4,
12
12
  height: 48,
@@ -14,6 +14,9 @@ export default {
14
14
  headerCell: {
15
15
  borderWidth: 2,
16
16
  gap: 8,
17
+ neutral: {
18
+ backgroundColor: '#f1efe4',
19
+ },
17
20
  paddingHorizontal: 12,
18
21
  paddingVertical: 16,
19
22
  },
@@ -3,4 +3,5 @@ export { default as formatThousands } from './formatThousands';
3
3
  export { default as getFlattenedColorValue } from './getFlattenedColorValue';
4
4
  export { default as getStyleValue } from './getStyleValue';
5
5
  export { default as hexWithOpacity } from './hexWithOpacity';
6
+ export { default as isEqual } from './isEqual';
6
7
  export * from './styleUtils';
@@ -0,0 +1,30 @@
1
+ const isEqual = (a: any, b: any): boolean => {
2
+ if (a === b) return true;
3
+
4
+ if (typeof a !== typeof b) return false;
5
+
6
+ if (typeof a !== 'object' || a === null || b === null) return false;
7
+
8
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
9
+
10
+ if (Array.isArray(a)) {
11
+ if (a.length !== b.length) return false;
12
+ for (let i = 0; i < a.length; i++) {
13
+ if (!isEqual(a[i], b[i])) return false;
14
+ }
15
+ return true;
16
+ }
17
+
18
+ const keysA = Object.keys(a);
19
+ const keysB = Object.keys(b);
20
+
21
+ if (keysA.length !== keysB.length) return false;
22
+
23
+ for (const key of keysA) {
24
+ if (!keysB.includes(key) || !isEqual(a[key], b[key])) return false;
25
+ }
26
+
27
+ return true;
28
+ };
29
+
30
+ export default isEqual;