@webority-technologies/mobile 0.0.15 → 0.0.20

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 (202) hide show
  1. package/lib/commonjs/components/Accordion/Accordion.js +60 -19
  2. package/lib/commonjs/components/AppBar/AppBar.js +29 -20
  3. package/lib/commonjs/components/Avatar/Avatar.js +38 -8
  4. package/lib/commonjs/components/Badge/Badge.js +66 -4
  5. package/lib/commonjs/components/Banner/Banner.js +146 -66
  6. package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +37 -15
  7. package/lib/commonjs/components/BottomSheet/BottomSheet.js +78 -53
  8. package/lib/commonjs/components/Button/Button.js +12 -5
  9. package/lib/commonjs/components/Card/Card.js +106 -16
  10. package/lib/commonjs/components/Carousel/Carousel.js +66 -12
  11. package/lib/commonjs/components/Checkbox/Checkbox.js +11 -7
  12. package/lib/commonjs/components/Chip/Chip.js +44 -12
  13. package/lib/commonjs/components/DatePicker/DatePicker.js +185 -76
  14. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +133 -59
  15. package/lib/commonjs/components/Dialog/Dialog.js +16 -10
  16. package/lib/commonjs/components/Drawer/Drawer.js +13 -10
  17. package/lib/commonjs/components/FieldBase/FieldBase.js +306 -0
  18. package/lib/commonjs/components/FieldBase/index.js +32 -0
  19. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +69 -44
  20. package/lib/commonjs/components/ForceUpdateDialog/ForceUpdateDialog.js +8 -2
  21. package/lib/commonjs/components/FormField/FormField.js +3 -2
  22. package/lib/commonjs/components/ImageGallery/ImageGallery.js +132 -44
  23. package/lib/commonjs/components/Input/Input.js +144 -181
  24. package/lib/commonjs/components/ListItem/ListItem.js +90 -11
  25. package/lib/commonjs/components/Modal/Modal.js +55 -27
  26. package/lib/commonjs/components/NumberInput/NumberInput.js +60 -106
  27. package/lib/commonjs/components/OTPInput/OTPInput.js +65 -58
  28. package/lib/commonjs/components/PickerTrigger/PickerTrigger.js +185 -0
  29. package/lib/commonjs/components/{AppIcon → PickerTrigger}/index.js +4 -4
  30. package/lib/commonjs/components/ProgressBar/ProgressBar.js +19 -11
  31. package/lib/commonjs/components/Radio/Radio.js +11 -6
  32. package/lib/commonjs/components/Rating/Rating.js +85 -19
  33. package/lib/commonjs/components/SearchBar/SearchBar.js +84 -107
  34. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +22 -11
  35. package/lib/commonjs/components/Select/Select.js +62 -91
  36. package/lib/commonjs/components/Skeleton/Skeleton.js +131 -174
  37. package/lib/commonjs/components/Skeleton/SkeletonClock.js +117 -0
  38. package/lib/commonjs/components/Skeleton/SkeletonContent.js +164 -81
  39. package/lib/commonjs/components/Skeleton/SkeletonProvider.js +72 -10
  40. package/lib/commonjs/components/Skeleton/index.js +17 -16
  41. package/lib/commonjs/components/Slider/Slider.js +44 -25
  42. package/lib/commonjs/components/Stepper/Stepper.js +199 -29
  43. package/lib/commonjs/components/Swipeable/Swipeable.js +36 -19
  44. package/lib/commonjs/components/Switch/Switch.js +9 -2
  45. package/lib/commonjs/components/Tabs/Tabs.js +84 -21
  46. package/lib/commonjs/components/TimePicker/TimePicker.js +123 -45
  47. package/lib/commonjs/components/Toast/Toast.js +27 -16
  48. package/lib/commonjs/components/Tooltip/Tooltip.js +56 -32
  49. package/lib/commonjs/components/index.js +37 -37
  50. package/lib/commonjs/theme/tokens.js +55 -7
  51. package/lib/module/components/Accordion/Accordion.js +61 -20
  52. package/lib/module/components/AppBar/AppBar.js +29 -20
  53. package/lib/module/components/Avatar/Avatar.js +39 -9
  54. package/lib/module/components/Badge/Badge.js +67 -5
  55. package/lib/module/components/Banner/Banner.js +147 -67
  56. package/lib/module/components/BottomNavigation/BottomNavigation.js +37 -15
  57. package/lib/module/components/BottomSheet/BottomSheet.js +80 -55
  58. package/lib/module/components/Button/Button.js +12 -5
  59. package/lib/module/components/Card/Card.js +107 -17
  60. package/lib/module/components/Carousel/Carousel.js +67 -13
  61. package/lib/module/components/Checkbox/Checkbox.js +11 -7
  62. package/lib/module/components/Chip/Chip.js +45 -13
  63. package/lib/module/components/DatePicker/DatePicker.js +185 -76
  64. package/lib/module/components/DateRangePicker/DateRangePicker.js +134 -60
  65. package/lib/module/components/Dialog/Dialog.js +16 -10
  66. package/lib/module/components/Drawer/Drawer.js +13 -10
  67. package/lib/module/components/FieldBase/FieldBase.js +297 -0
  68. package/lib/module/components/FieldBase/index.js +4 -0
  69. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +69 -44
  70. package/lib/module/components/ForceUpdateDialog/ForceUpdateDialog.js +8 -2
  71. package/lib/module/components/FormField/FormField.js +3 -2
  72. package/lib/module/components/ImageGallery/ImageGallery.js +128 -40
  73. package/lib/module/components/Input/Input.js +144 -179
  74. package/lib/module/components/ListItem/ListItem.js +91 -12
  75. package/lib/module/components/Modal/Modal.js +55 -27
  76. package/lib/module/components/NumberInput/NumberInput.js +60 -106
  77. package/lib/module/components/OTPInput/OTPInput.js +65 -58
  78. package/lib/module/components/PickerTrigger/PickerTrigger.js +181 -0
  79. package/lib/module/components/PickerTrigger/index.js +4 -0
  80. package/lib/module/components/ProgressBar/ProgressBar.js +19 -11
  81. package/lib/module/components/Radio/Radio.js +11 -6
  82. package/lib/module/components/Rating/Rating.js +86 -20
  83. package/lib/module/components/SearchBar/SearchBar.js +84 -107
  84. package/lib/module/components/SegmentedControl/SegmentedControl.js +22 -11
  85. package/lib/module/components/Select/Select.js +62 -91
  86. package/lib/module/components/Skeleton/Skeleton.js +135 -175
  87. package/lib/module/components/Skeleton/SkeletonClock.js +110 -0
  88. package/lib/module/components/Skeleton/SkeletonContent.js +167 -84
  89. package/lib/module/components/Skeleton/SkeletonProvider.js +71 -10
  90. package/lib/module/components/Skeleton/index.js +3 -2
  91. package/lib/module/components/Slider/Slider.js +44 -25
  92. package/lib/module/components/Stepper/Stepper.js +201 -31
  93. package/lib/module/components/Swipeable/Swipeable.js +36 -19
  94. package/lib/module/components/Switch/Switch.js +9 -2
  95. package/lib/module/components/Tabs/Tabs.js +84 -21
  96. package/lib/module/components/TimePicker/TimePicker.js +123 -45
  97. package/lib/module/components/Toast/Toast.js +27 -16
  98. package/lib/module/components/Tooltip/Tooltip.js +56 -32
  99. package/lib/module/components/index.js +2 -2
  100. package/lib/module/theme/tokens.js +55 -7
  101. package/lib/typescript/commonjs/components/Accordion/Accordion.d.ts +10 -5
  102. package/lib/typescript/commonjs/components/AppBar/AppBar.d.ts +8 -0
  103. package/lib/typescript/commonjs/components/Avatar/Avatar.d.ts +12 -6
  104. package/lib/typescript/commonjs/components/Badge/Badge.d.ts +7 -6
  105. package/lib/typescript/commonjs/components/Banner/Banner.d.ts +17 -6
  106. package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +7 -0
  107. package/lib/typescript/commonjs/components/Card/Card.d.ts +17 -6
  108. package/lib/typescript/commonjs/components/Carousel/Carousel.d.ts +7 -6
  109. package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +9 -1
  110. package/lib/typescript/commonjs/components/Chip/Chip.d.ts +13 -6
  111. package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +38 -3
  112. package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +36 -3
  113. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +13 -1
  114. package/lib/typescript/commonjs/components/FieldBase/FieldBase.d.ts +141 -0
  115. package/lib/typescript/commonjs/components/FieldBase/index.d.ts +3 -0
  116. package/lib/typescript/commonjs/components/FloatingActionButton/FloatingActionButton.d.ts +8 -6
  117. package/lib/typescript/commonjs/components/FloatingActionButton/index.d.ts +1 -1
  118. package/lib/typescript/commonjs/components/ForceUpdateDialog/ForceUpdateDialog.d.ts +7 -0
  119. package/lib/typescript/commonjs/components/FormField/FormField.d.ts +7 -0
  120. package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -4
  121. package/lib/typescript/commonjs/components/Input/Input.d.ts +6 -0
  122. package/lib/typescript/commonjs/components/ListItem/ListItem.d.ts +13 -6
  123. package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -0
  124. package/lib/typescript/commonjs/components/PickerTrigger/PickerTrigger.d.ts +57 -0
  125. package/lib/typescript/commonjs/components/PickerTrigger/index.d.ts +3 -0
  126. package/lib/typescript/commonjs/components/ProgressBar/ProgressBar.d.ts +2 -0
  127. package/lib/typescript/commonjs/components/Radio/Radio.d.ts +3 -0
  128. package/lib/typescript/commonjs/components/Rating/Rating.d.ts +9 -6
  129. package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  130. package/lib/typescript/commonjs/components/Skeleton/Skeleton.d.ts +49 -20
  131. package/lib/typescript/commonjs/components/Skeleton/SkeletonClock.d.ts +60 -0
  132. package/lib/typescript/commonjs/components/Skeleton/SkeletonContent.d.ts +80 -19
  133. package/lib/typescript/commonjs/components/Skeleton/SkeletonProvider.d.ts +39 -5
  134. package/lib/typescript/commonjs/components/Skeleton/index.d.ts +6 -4
  135. package/lib/typescript/commonjs/components/Slider/Slider.d.ts +12 -1
  136. package/lib/typescript/commonjs/components/Stepper/Stepper.d.ts +18 -6
  137. package/lib/typescript/commonjs/components/Swipeable/Swipeable.d.ts +2 -0
  138. package/lib/typescript/commonjs/components/Switch/Switch.d.ts +1 -0
  139. package/lib/typescript/commonjs/components/Tabs/Tabs.d.ts +26 -2
  140. package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +36 -3
  141. package/lib/typescript/commonjs/components/Toast/Toast.d.ts +8 -0
  142. package/lib/typescript/commonjs/components/Tooltip/Tooltip.d.ts +7 -1
  143. package/lib/typescript/commonjs/components/index.d.ts +5 -5
  144. package/lib/typescript/commonjs/index.d.ts +1 -1
  145. package/lib/typescript/commonjs/theme/index.d.ts +1 -1
  146. package/lib/typescript/commonjs/theme/types.d.ts +553 -11
  147. package/lib/typescript/module/components/Accordion/Accordion.d.ts +10 -5
  148. package/lib/typescript/module/components/AppBar/AppBar.d.ts +8 -0
  149. package/lib/typescript/module/components/Avatar/Avatar.d.ts +12 -6
  150. package/lib/typescript/module/components/Badge/Badge.d.ts +7 -6
  151. package/lib/typescript/module/components/Banner/Banner.d.ts +17 -6
  152. package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +7 -0
  153. package/lib/typescript/module/components/Card/Card.d.ts +17 -6
  154. package/lib/typescript/module/components/Carousel/Carousel.d.ts +7 -6
  155. package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +9 -1
  156. package/lib/typescript/module/components/Chip/Chip.d.ts +13 -6
  157. package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +38 -3
  158. package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +36 -3
  159. package/lib/typescript/module/components/Dialog/Dialog.d.ts +13 -1
  160. package/lib/typescript/module/components/FieldBase/FieldBase.d.ts +141 -0
  161. package/lib/typescript/module/components/FieldBase/index.d.ts +3 -0
  162. package/lib/typescript/module/components/FloatingActionButton/FloatingActionButton.d.ts +8 -6
  163. package/lib/typescript/module/components/FloatingActionButton/index.d.ts +1 -1
  164. package/lib/typescript/module/components/ForceUpdateDialog/ForceUpdateDialog.d.ts +7 -0
  165. package/lib/typescript/module/components/FormField/FormField.d.ts +7 -0
  166. package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -4
  167. package/lib/typescript/module/components/Input/Input.d.ts +6 -0
  168. package/lib/typescript/module/components/ListItem/ListItem.d.ts +13 -6
  169. package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -0
  170. package/lib/typescript/module/components/PickerTrigger/PickerTrigger.d.ts +57 -0
  171. package/lib/typescript/module/components/PickerTrigger/index.d.ts +3 -0
  172. package/lib/typescript/module/components/ProgressBar/ProgressBar.d.ts +2 -0
  173. package/lib/typescript/module/components/Radio/Radio.d.ts +3 -0
  174. package/lib/typescript/module/components/Rating/Rating.d.ts +9 -6
  175. package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  176. package/lib/typescript/module/components/Skeleton/Skeleton.d.ts +49 -20
  177. package/lib/typescript/module/components/Skeleton/SkeletonClock.d.ts +60 -0
  178. package/lib/typescript/module/components/Skeleton/SkeletonContent.d.ts +80 -19
  179. package/lib/typescript/module/components/Skeleton/SkeletonProvider.d.ts +39 -5
  180. package/lib/typescript/module/components/Skeleton/index.d.ts +6 -4
  181. package/lib/typescript/module/components/Slider/Slider.d.ts +12 -1
  182. package/lib/typescript/module/components/Stepper/Stepper.d.ts +18 -6
  183. package/lib/typescript/module/components/Swipeable/Swipeable.d.ts +2 -0
  184. package/lib/typescript/module/components/Switch/Switch.d.ts +1 -0
  185. package/lib/typescript/module/components/Tabs/Tabs.d.ts +26 -2
  186. package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +36 -3
  187. package/lib/typescript/module/components/Toast/Toast.d.ts +8 -0
  188. package/lib/typescript/module/components/Tooltip/Tooltip.d.ts +7 -1
  189. package/lib/typescript/module/components/index.d.ts +5 -5
  190. package/lib/typescript/module/index.d.ts +1 -1
  191. package/lib/typescript/module/theme/index.d.ts +1 -1
  192. package/lib/typescript/module/theme/types.d.ts +553 -11
  193. package/package.json +2 -6
  194. package/lib/commonjs/components/AppIcon/AppIcon.js +0 -120
  195. package/lib/commonjs/types/vector-icons.d.js +0 -2
  196. package/lib/module/components/AppIcon/AppIcon.js +0 -111
  197. package/lib/module/components/AppIcon/index.js +0 -4
  198. package/lib/module/types/vector-icons.d.js +0 -2
  199. package/lib/typescript/commonjs/components/AppIcon/AppIcon.d.ts +0 -20
  200. package/lib/typescript/commonjs/components/AppIcon/index.d.ts +0 -3
  201. package/lib/typescript/module/components/AppIcon/AppIcon.d.ts +0 -20
  202. package/lib/typescript/module/components/AppIcon/index.d.ts +0 -3
@@ -2,8 +2,25 @@
2
2
 
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
6
7
  import { triggerHaptic } from "../../utils/index.js";
8
+ import { PickerTrigger } from "../PickerTrigger/index.js";
9
+
10
+ /**
11
+ * DatePicker operates in two modes:
12
+ *
13
+ * 1. **Controlled-modal mode** — pass `visible` (plus `onSelect` / `onClose`)
14
+ * and own the open state externally. The component renders only the modal.
15
+ * 2. **Trigger mode** — omit `visible`. The component renders a built-in
16
+ * PickerTrigger field (label / value / placeholder / chevron / clear /
17
+ * helper / error / size / variant) and manages its own open state. The
18
+ * field opens the modal on press and closes it on confirm/cancel.
19
+ *
20
+ * `onSelect` / `onClose` are typed optional to support trigger-only usage
21
+ * where the consumer may not need either callback. In controlled-modal mode
22
+ * they remain semantically required.
23
+ */
7
24
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
8
25
  const DAY_MS = 24 * 60 * 60 * 1000;
9
26
  const GRID_SIZE = 12; // 4 cols x 3 rows for year / decade grids
@@ -81,7 +98,6 @@ const buildDecadeCells = anchorYear => {
81
98
  };
82
99
  const DatePicker = props => {
83
100
  const {
84
- visible,
85
101
  value,
86
102
  onSelect,
87
103
  onClose,
@@ -95,9 +111,32 @@ const DatePicker = props => {
95
111
  confirmLabel = 'Confirm',
96
112
  cancelLabel = 'Cancel',
97
113
  style,
98
- testID
114
+ containerStyle,
115
+ headerLabelStyle,
116
+ navButtonStyle,
117
+ footerButtonStyle,
118
+ testID,
119
+ label,
120
+ placeholder,
121
+ helperText,
122
+ error,
123
+ required,
124
+ disabled,
125
+ size,
126
+ variant,
127
+ clearable,
128
+ onClear,
129
+ formatValue,
130
+ triggerStyle
99
131
  } = props;
132
+
133
+ // Controlled vs uncontrolled detection: if `visible` is supplied (even
134
+ // `false`), the consumer owns the open state; otherwise we own it.
135
+ const isControlled = props.visible !== undefined;
136
+ const [internalOpen, setInternalOpen] = useState(false);
137
+ const open = isControlled ? props.visible : internalOpen;
100
138
  const theme = useTheme();
139
+ const insets = useSafeAreaInsets();
101
140
  const styles = useMemo(() => buildStyles(theme), [theme]);
102
141
  const today = useMemo(() => startOfDay(new Date()), []);
103
142
  const initialAnchor = value ? startOfDay(value) : today;
@@ -126,17 +165,20 @@ const DatePicker = props => {
126
165
  }
127
166
  }, [value]);
128
167
 
129
- // Modal open / close animation.
168
+ // Modal open / close animation. Backdrop opacity runs on JS driver — see
169
+ // Modal.tsx for the RN 0.85 / Fabric two-bug reason (view flattening +
170
+ // native-driver opacity on Animated.View with backgroundColor). Sheet
171
+ // transform stays native.
130
172
  useEffect(() => {
131
173
  if (mode !== 'modal') return;
132
- if (visible) {
133
- setNativeValue(backdrop, 0);
174
+ if (open) {
175
+ backdrop.setValue(0);
134
176
  setNativeValue(sheet, 0);
135
177
  Animated.parallel([Animated.timing(backdrop, {
136
178
  toValue: 1,
137
179
  duration: theme.motion.duration.normal,
138
180
  easing: Easing.out(Easing.cubic),
139
- useNativeDriver: true
181
+ useNativeDriver: false
140
182
  }), Animated.spring(sheet, {
141
183
  toValue: 1,
142
184
  damping: theme.motion.spring.gentle.damping,
@@ -145,7 +187,7 @@ const DatePicker = props => {
145
187
  useNativeDriver: true
146
188
  })]).start();
147
189
  }
148
- }, [visible, mode, backdrop, sheet, theme.motion]);
190
+ }, [open, mode, backdrop, sheet, theme.motion]);
149
191
  const disabledIsoSet = useMemo(() => {
150
192
  const set = new Set();
151
193
  disabledDates?.forEach(d => set.add(toIso(startOfDay(d))));
@@ -186,17 +228,19 @@ const DatePicker = props => {
186
228
  return false;
187
229
  }, [minDay, maxDay]);
188
230
  const animateMonthChange = useCallback(delta => {
189
- triggerHaptic('impactLight');
231
+ if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
190
232
  const direction = delta > 0 ? 1 : -1;
191
- const distance = 32;
233
+ const distance = theme.components.datePicker?.monthSlideDistance ?? 32;
234
+ const outDuration = theme.components.datePicker?.monthSlideOutDuration ?? theme.motion.duration.fast ?? 140;
235
+ const inDuration = theme.components.datePicker?.monthSlideInDuration ?? theme.motion.duration.fast ?? 160;
192
236
  Animated.sequence([Animated.parallel([Animated.timing(monthSlide, {
193
237
  toValue: -direction * distance,
194
- duration: 140,
238
+ duration: outDuration,
195
239
  easing: Easing.out(Easing.quad),
196
240
  useNativeDriver: true
197
241
  }), Animated.timing(monthFade, {
198
242
  toValue: 0,
199
- duration: 140,
243
+ duration: outDuration,
200
244
  useNativeDriver: true
201
245
  })]), Animated.timing(monthSlide, {
202
246
  toValue: direction * distance,
@@ -204,68 +248,71 @@ const DatePicker = props => {
204
248
  useNativeDriver: true
205
249
  }), Animated.parallel([Animated.timing(monthSlide, {
206
250
  toValue: 0,
207
- duration: 160,
251
+ duration: inDuration,
208
252
  easing: Easing.out(Easing.cubic),
209
253
  useNativeDriver: true
210
254
  }), Animated.timing(monthFade, {
211
255
  toValue: 1,
212
- duration: 160,
256
+ duration: inDuration,
213
257
  useNativeDriver: true
214
258
  })])]).start();
215
259
  const next = addMonths(anchor, delta);
216
260
  setAnchor(next);
217
261
  AccessibilityInfo.announceForAccessibility(formatMonthYear(next, locale));
218
- }, [anchor, locale, monthFade, monthSlide]);
262
+ }, [anchor, locale, monthFade, monthSlide, theme.components.datePicker, theme.motion.duration.fast]);
219
263
  const goPrev = useCallback(() => {
220
264
  if (viewMode === 'days') {
221
265
  animateMonthChange(-1);
222
266
  return;
223
267
  }
224
- triggerHaptic('impactLight');
268
+ if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
225
269
  if (viewMode === 'years') {
226
270
  setAnchor(prev => new Date(prev.getFullYear() - GRID_SIZE, prev.getMonth(), 1));
227
271
  } else {
228
272
  setAnchor(prev => new Date(prev.getFullYear() - GRID_SIZE * DECADE_SPAN, prev.getMonth(), 1));
229
273
  }
230
- }, [animateMonthChange, viewMode]);
274
+ }, [animateMonthChange, viewMode, theme.components.datePicker]);
231
275
  const goNext = useCallback(() => {
232
276
  if (viewMode === 'days') {
233
277
  animateMonthChange(1);
234
278
  return;
235
279
  }
236
- triggerHaptic('impactLight');
280
+ if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
237
281
  if (viewMode === 'years') {
238
282
  setAnchor(prev => new Date(prev.getFullYear() + GRID_SIZE, prev.getMonth(), 1));
239
283
  } else {
240
284
  setAnchor(prev => new Date(prev.getFullYear() + GRID_SIZE * DECADE_SPAN, prev.getMonth(), 1));
241
285
  }
242
- }, [animateMonthChange, viewMode]);
286
+ }, [animateMonthChange, viewMode, theme.components.datePicker]);
243
287
 
244
- // View-mode transition: fade + scale (200ms, native driver).
288
+ // View-mode transition: fade + scale, native driver.
245
289
  const animateViewTransition = useCallback(() => {
290
+ const dur = theme.components.datePicker?.viewTransitionDuration ?? theme.motion.duration.normal ?? 200;
246
291
  setNativeValue(viewFade, 0);
247
292
  setNativeValue(viewScale, 0.9);
248
293
  Animated.parallel([Animated.timing(viewFade, {
249
294
  toValue: 1,
250
- duration: 200,
295
+ duration: dur,
251
296
  easing: Easing.out(Easing.cubic),
252
297
  useNativeDriver: true
253
298
  }), Animated.timing(viewScale, {
254
299
  toValue: 1,
255
- duration: 200,
300
+ duration: dur,
256
301
  easing: Easing.out(Easing.cubic),
257
302
  useNativeDriver: true
258
303
  })]).start();
259
- }, [viewFade, viewScale]);
304
+ }, [viewFade, viewScale, theme.components.datePicker, theme.motion.duration.normal]);
260
305
  const cycleViewMode = useCallback(() => {
261
- triggerHaptic('selection');
306
+ if (theme.components.datePicker?.haptic) triggerHaptic('selection');
307
+ const pressDur = theme.components.datePicker?.headerPressDuration ?? 80;
308
+ const releaseDur = theme.components.datePicker?.headerReleaseDuration ?? 100;
262
309
  Animated.sequence([Animated.timing(headerScale, {
263
310
  toValue: 0.96,
264
- duration: 80,
311
+ duration: pressDur,
265
312
  useNativeDriver: true
266
313
  }), Animated.timing(headerScale, {
267
314
  toValue: 1,
268
- duration: 100,
315
+ duration: releaseDur,
269
316
  useNativeDriver: true
270
317
  })]).start();
271
318
  setViewMode(prev => {
@@ -274,23 +321,23 @@ const DatePicker = props => {
274
321
  return 'years';
275
322
  });
276
323
  animateViewTransition();
277
- }, [animateViewTransition, headerScale]);
324
+ }, [animateViewTransition, headerScale, theme.components.datePicker]);
278
325
  const pickYear = useCallback(year => {
279
- triggerHaptic('selection');
326
+ if (theme.components.datePicker?.haptic) triggerHaptic('selection');
280
327
  setAnchor(prev => new Date(year, prev.getMonth(), 1));
281
328
  setViewMode('days');
282
329
  animateViewTransition();
283
- }, [animateViewTransition]);
330
+ }, [animateViewTransition, theme.components.datePicker]);
284
331
  const pickDecade = useCallback(decadeStart => {
285
- triggerHaptic('selection');
332
+ if (theme.components.datePicker?.haptic) triggerHaptic('selection');
286
333
  // Anchor mid-decade so the year grid centers nicely on this decade.
287
334
  setAnchor(prev => new Date(decadeStart + 4, prev.getMonth(), 1));
288
335
  setViewMode('years');
289
336
  animateViewTransition();
290
- }, [animateViewTransition]);
337
+ }, [animateViewTransition, theme.components.datePicker]);
291
338
  const pressDay = useCallback(cell => {
292
339
  if (isDisabled(cell.date)) return;
293
- triggerHaptic('selection');
340
+ if (theme.components.datePicker?.haptic) triggerHaptic('selection');
294
341
  setPendingDate(cell.date);
295
342
  if (!cell.inMonth) {
296
343
  setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
@@ -303,42 +350,50 @@ const DatePicker = props => {
303
350
  mass: theme.motion.spring.bouncy.mass,
304
351
  useNativeDriver: true
305
352
  }).start();
306
- }, [isDisabled, selectScale, theme.motion.spring.bouncy]);
353
+ }, [isDisabled, selectScale, theme.motion.spring.bouncy, theme.components.datePicker]);
354
+ const finalizeClose = useCallback(() => {
355
+ if (isControlled) {
356
+ onClose?.();
357
+ } else {
358
+ setInternalOpen(false);
359
+ onClose?.();
360
+ }
361
+ }, [isControlled, onClose]);
307
362
  const handleClose = useCallback(() => {
308
363
  if (mode !== 'modal') {
309
- onClose();
364
+ finalizeClose();
310
365
  return;
311
366
  }
312
367
  Animated.parallel([Animated.timing(backdrop, {
313
368
  toValue: 0,
314
369
  duration: theme.motion.duration.fast,
315
370
  easing: Easing.in(Easing.cubic),
316
- useNativeDriver: true
371
+ useNativeDriver: false
317
372
  }), Animated.timing(sheet, {
318
373
  toValue: 0,
319
374
  duration: theme.motion.duration.fast,
320
375
  easing: Easing.in(Easing.cubic),
321
376
  useNativeDriver: true
322
377
  })]).start(() => {
323
- onClose();
378
+ finalizeClose();
324
379
  });
325
- }, [backdrop, mode, onClose, sheet, theme.motion.duration.fast]);
380
+ }, [backdrop, mode, finalizeClose, sheet, theme.motion.duration.fast]);
326
381
  const handleCancel = useCallback(() => {
327
- triggerHaptic('selection');
382
+ if (theme.components.datePicker?.haptic) triggerHaptic('selection');
328
383
  handleClose();
329
- }, [handleClose]);
384
+ }, [handleClose, theme.components.datePicker]);
330
385
  const handleConfirm = useCallback(() => {
331
386
  if (!pendingDate) return;
332
- triggerHaptic('notificationSuccess');
333
- onSelect(pendingDate);
387
+ if (theme.components.datePicker?.haptic) triggerHaptic('notificationSuccess');
388
+ onSelect?.(pendingDate);
334
389
  handleClose();
335
- }, [handleClose, onSelect, pendingDate]);
390
+ }, [handleClose, onSelect, pendingDate, theme.components.datePicker]);
336
391
  const sheetTranslate = sheet.interpolate({
337
392
  inputRange: [0, 1],
338
393
  outputRange: [320, 0]
339
394
  });
340
395
  const calendar = /*#__PURE__*/_jsxs(View, {
341
- style: [styles.card, style],
396
+ style: [styles.card, style, containerStyle],
342
397
  accessibilityRole: mode === 'modal' ? undefined : 'none',
343
398
  accessible: false,
344
399
  testID: testID,
@@ -355,7 +410,7 @@ const DatePicker = props => {
355
410
  onPress: goPrev,
356
411
  style: ({
357
412
  pressed
358
- }) => [styles.navBtn, pressed && styles.navBtnPressed],
413
+ }) => [styles.navBtn, pressed && styles.navBtnPressed, navButtonStyle],
359
414
  accessibilityRole: "button",
360
415
  accessibilityLabel: viewMode === 'days' ? 'Previous month' : viewMode === 'years' ? 'Previous years' : 'Previous decades',
361
416
  hitSlop: 8,
@@ -365,7 +420,7 @@ const DatePicker = props => {
365
420
  })
366
421
  }), /*#__PURE__*/_jsx(Pressable, {
367
422
  onPress: cycleViewMode,
368
- style: styles.headerLabelWrap,
423
+ style: [styles.headerLabelWrap, headerLabelStyle],
369
424
  accessibilityRole: "button",
370
425
  accessibilityLabel: viewMode === 'days' ? `${headerLabel}, switch to year selection` : viewMode === 'years' ? `${headerLabel}, switch to decade selection` : `${headerLabel}, switch to year selection`,
371
426
  hitSlop: 8,
@@ -399,7 +454,7 @@ const DatePicker = props => {
399
454
  onPress: goNext,
400
455
  style: ({
401
456
  pressed
402
- }) => [styles.navBtn, pressed && styles.navBtnPressed],
457
+ }) => [styles.navBtn, pressed && styles.navBtnPressed, navButtonStyle],
403
458
  accessibilityRole: "button",
404
459
  accessibilityLabel: viewMode === 'days' ? 'Next month' : viewMode === 'years' ? 'Next years' : 'Next decades',
405
460
  hitSlop: 8,
@@ -573,7 +628,7 @@ const DatePicker = props => {
573
628
  onPress: handleCancel,
574
629
  style: ({
575
630
  pressed
576
- }) => [styles.footerBtn, styles.footerCancel, pressed && styles.footerBtnPressed],
631
+ }) => [styles.footerBtn, styles.footerCancel, pressed && styles.footerBtnPressed, footerButtonStyle],
577
632
  accessibilityRole: "button",
578
633
  accessibilityLabel: cancelLabel,
579
634
  children: /*#__PURE__*/_jsx(Text, {
@@ -592,7 +647,7 @@ const DatePicker = props => {
592
647
  opacity: !pendingDate ? 0.5 : 1
593
648
  }, pressed && {
594
649
  backgroundColor: theme.colors.primaryHover
595
- }],
650
+ }, footerButtonStyle],
596
651
  accessibilityRole: "button",
597
652
  accessibilityLabel: confirmLabel,
598
653
  accessibilityState: {
@@ -607,38 +662,92 @@ const DatePicker = props => {
607
662
  })]
608
663
  })]
609
664
  });
665
+ const formatTriggerValue = formatValue ?? (d => d.toLocaleDateString());
666
+ const triggerValueText = value ? formatTriggerValue(value) : undefined;
667
+ const showTrigger = !isControlled;
610
668
  if (mode === 'inline') {
611
- if (!visible) return null;
669
+ if (!open && !showTrigger) return null;
670
+ if (showTrigger) {
671
+ return /*#__PURE__*/_jsxs(_Fragment, {
672
+ children: [/*#__PURE__*/_jsx(PickerTrigger, {
673
+ label: label,
674
+ value: triggerValueText,
675
+ placeholder: placeholder,
676
+ helperText: helperText,
677
+ error: error,
678
+ required: required,
679
+ disabled: disabled,
680
+ size: size,
681
+ variant: variant,
682
+ clearable: clearable,
683
+ onClear: onClear,
684
+ onPress: () => setInternalOpen(true),
685
+ triggerStyle: triggerStyle
686
+ }), open ? calendar : null]
687
+ });
688
+ }
612
689
  return calendar;
613
690
  }
614
- return /*#__PURE__*/_jsxs(Modal, {
615
- visible: visible,
691
+ const modalNode = /*#__PURE__*/_jsx(Modal, {
692
+ visible: open,
616
693
  transparent: true,
617
- statusBarTranslucent: true,
694
+ statusBarTranslucent: true
695
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
696
+ // @ts-ignore — Android-only RN 0.71+; iOS ignores it.
697
+ ,
698
+ navigationBarTranslucent: true,
618
699
  animationType: "none",
619
700
  onRequestClose: handleClose,
620
- children: [/*#__PURE__*/_jsx(Animated.View, {
701
+ children: /*#__PURE__*/_jsxs(View
702
+ // Backdrop wraps the sheet (not sibling) — on Fabric a sibling
703
+ // backdrop is occluded by the sheet wrapper regardless of order.
704
+ // collapsable={false} prevents flattening; plain View + pre-baked
705
+ // rgba colour avoids the Fabric animated-opacity-on-coloured-View bug.
706
+ , {
707
+ collapsable: false,
621
708
  style: [styles.backdrop, {
622
- opacity: backdrop,
623
709
  backgroundColor: theme.colors.background.overlay
624
710
  }],
625
- children: /*#__PURE__*/_jsx(Pressable, {
711
+ children: [/*#__PURE__*/_jsx(Pressable, {
626
712
  style: StyleSheet.absoluteFill,
627
713
  onPress: handleClose,
628
714
  accessibilityLabel: "Dismiss"
629
- })
630
- }), /*#__PURE__*/_jsx(Animated.View, {
631
- style: [styles.sheetWrap, {
632
- opacity: sheet,
633
- transform: [{
634
- translateY: sheetTranslate
635
- }]
636
- }],
637
- accessibilityViewIsModal: true,
638
- accessibilityRole: "none",
639
- children: calendar
640
- })]
715
+ }), /*#__PURE__*/_jsx(Animated.View, {
716
+ style: [styles.sheetWrap, {
717
+ // Push the sheet up by the system nav-bar inset so its content
718
+ // never lands behind Android gesture/3-button chrome.
719
+ paddingBottom: insets.bottom + theme.spacing.lg,
720
+ opacity: sheet,
721
+ transform: [{
722
+ translateY: sheetTranslate
723
+ }]
724
+ }],
725
+ accessibilityViewIsModal: true,
726
+ accessibilityRole: "none",
727
+ children: calendar
728
+ })]
729
+ })
641
730
  });
731
+ if (showTrigger) {
732
+ return /*#__PURE__*/_jsxs(_Fragment, {
733
+ children: [/*#__PURE__*/_jsx(PickerTrigger, {
734
+ label: label,
735
+ value: triggerValueText,
736
+ placeholder: placeholder,
737
+ helperText: helperText,
738
+ error: error,
739
+ required: required,
740
+ disabled: disabled,
741
+ size: size,
742
+ variant: variant,
743
+ clearable: clearable,
744
+ onClear: onClear,
745
+ onPress: () => setInternalOpen(true),
746
+ triggerStyle: triggerStyle
747
+ }), modalNode]
748
+ });
749
+ }
750
+ return modalNode;
642
751
  };
643
752
  const buildStyles = theme => {
644
753
  const cardShadow = theme.shadows.lg;
@@ -679,8 +788,8 @@ const buildStyles = theme => {
679
788
  marginBottom: theme.spacing.md
680
789
  },
681
790
  navBtn: {
682
- width: 36,
683
- height: 36,
791
+ width: theme.components.datePicker?.navButtonSize ?? 36,
792
+ height: theme.components.datePicker?.navButtonSize ?? 36,
684
793
  borderRadius: theme.radius.full,
685
794
  alignItems: 'center',
686
795
  justifyContent: 'center',
@@ -690,8 +799,8 @@ const buildStyles = theme => {
690
799
  backgroundColor: theme.colors.surface.pressed
691
800
  },
692
801
  navText: {
693
- fontSize: 22,
694
- lineHeight: 24,
802
+ fontSize: theme.components.datePicker?.navTextFontSize ?? 22,
803
+ lineHeight: theme.components.datePicker?.navTextLineHeight ?? 24,
695
804
  color: theme.colors.text.primary,
696
805
  fontWeight: theme.typography.fontWeight.semibold
697
806
  },
@@ -740,12 +849,12 @@ const buildStyles = theme => {
740
849
  aspectRatio: 1,
741
850
  alignItems: 'center',
742
851
  justifyContent: 'center',
743
- padding: 2
852
+ padding: theme.components.datePicker?.dayCellPadding ?? 2
744
853
  },
745
854
  dayInner: {
746
855
  width: '100%',
747
856
  height: '100%',
748
- borderRadius: theme.radius.full,
857
+ borderRadius: theme.components.datePicker?.dayInnerBorderRadius ?? theme.radius.full,
749
858
  alignItems: 'center',
750
859
  justifyContent: 'center'
751
860
  },
@@ -761,12 +870,12 @@ const buildStyles = theme => {
761
870
  aspectRatio: 1.6,
762
871
  alignItems: 'center',
763
872
  justifyContent: 'center',
764
- padding: 4
873
+ padding: theme.components.datePicker?.yearCellPadding ?? 4
765
874
  },
766
875
  yearInner: {
767
876
  width: '100%',
768
877
  height: '100%',
769
- borderRadius: theme.radius.lg,
878
+ borderRadius: theme.components.datePicker?.yearInnerBorderRadius ?? theme.radius.lg,
770
879
  alignItems: 'center',
771
880
  justifyContent: 'center'
772
881
  },
@@ -777,7 +886,7 @@ const buildStyles = theme => {
777
886
  decadeRangeText: {
778
887
  fontSize: theme.typography.fontSize.xs,
779
888
  fontWeight: theme.typography.fontWeight.medium,
780
- marginTop: 2
889
+ marginTop: theme.components.datePicker?.decadeRangeMarginTop ?? 2
781
890
  },
782
891
  footer: {
783
892
  flexDirection: 'row',
@@ -790,7 +899,7 @@ const buildStyles = theme => {
790
899
  paddingHorizontal: theme.spacing.lg,
791
900
  paddingVertical: theme.spacing.sm,
792
901
  borderRadius: theme.radius.md,
793
- minHeight: 40,
902
+ minHeight: theme.components.datePicker?.footerButtonMinHeight ?? 40,
794
903
  alignItems: 'center',
795
904
  justifyContent: 'center'
796
905
  },