rn-grid-date-time-picker 1.0.0 → 1.2.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 (384) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +100 -638
  3. package/lib/commonjs/adapters/utcMidnight.js +15 -6
  4. package/lib/commonjs/adapters/utcMidnight.js.map +1 -1
  5. package/lib/commonjs/components/DateTimePicker.js +484 -85
  6. package/lib/commonjs/components/DateTimePicker.js.map +1 -1
  7. package/lib/commonjs/components/calendar/Calendar.js +21 -7
  8. package/lib/commonjs/components/calendar/Calendar.js.map +1 -1
  9. package/lib/commonjs/components/calendar/CalendarHeader.js +46 -6
  10. package/lib/commonjs/components/calendar/CalendarHeader.js.map +1 -1
  11. package/lib/commonjs/components/calendar/CalendarSwitch.js +150 -0
  12. package/lib/commonjs/components/calendar/CalendarSwitch.js.map +1 -0
  13. package/lib/commonjs/components/calendar/DayCell.js +8 -3
  14. package/lib/commonjs/components/calendar/DayCell.js.map +1 -1
  15. package/lib/commonjs/components/calendar/DayGrid.js +13 -2
  16. package/lib/commonjs/components/calendar/DayGrid.js.map +1 -1
  17. package/lib/commonjs/components/calendar/PeriodGrid.js +3 -1
  18. package/lib/commonjs/components/calendar/PeriodGrid.js.map +1 -1
  19. package/lib/commonjs/components/calendar/calendarStyles.js +3 -2
  20. package/lib/commonjs/components/calendar/calendarStyles.js.map +1 -1
  21. package/lib/commonjs/components/calendar/localeNames.js +46 -24
  22. package/lib/commonjs/components/calendar/localeNames.js.map +1 -1
  23. package/lib/commonjs/components/calendar/selection.js +9 -1
  24. package/lib/commonjs/components/calendar/selection.js.map +1 -1
  25. package/lib/commonjs/components/container/DefaultSheet.js +42 -11
  26. package/lib/commonjs/components/container/DefaultSheet.js.map +1 -1
  27. package/lib/commonjs/components/container/PickerContainer.js +4 -0
  28. package/lib/commonjs/components/container/PickerContainer.js.map +1 -1
  29. package/lib/commonjs/components/container/PickerLayout.js +1 -1
  30. package/lib/commonjs/components/container/PickerLayout.js.map +1 -1
  31. package/lib/commonjs/components/container/containerStyles.js +12 -4
  32. package/lib/commonjs/components/container/containerStyles.js.map +1 -1
  33. package/lib/commonjs/components/field/RangeFields.js +5 -1
  34. package/lib/commonjs/components/field/RangeFields.js.map +1 -1
  35. package/lib/commonjs/components/field/SegmentedInput.js +8 -3
  36. package/lib/commonjs/components/field/SegmentedInput.js.map +1 -1
  37. package/lib/commonjs/components/field/ValueField.js +64 -8
  38. package/lib/commonjs/components/field/ValueField.js.map +1 -1
  39. package/lib/commonjs/components/field/fieldStyles.js +17 -0
  40. package/lib/commonjs/components/field/fieldStyles.js.map +1 -1
  41. package/lib/commonjs/components/field/fieldText.js +29 -13
  42. package/lib/commonjs/components/field/fieldText.js.map +1 -1
  43. package/lib/commonjs/components/hooks/useBottomInset.js +32 -0
  44. package/lib/commonjs/components/hooks/useBottomInset.js.map +1 -0
  45. package/lib/commonjs/components/hooks/useKeyboardFold.js +36 -13
  46. package/lib/commonjs/components/hooks/useKeyboardFold.js.map +1 -1
  47. package/lib/commonjs/components/hooks/useOpenPicker.js +39 -17
  48. package/lib/commonjs/components/hooks/useOpenPicker.js.map +1 -1
  49. package/lib/commonjs/components/hooks/useTypedSegments.js +6 -2
  50. package/lib/commonjs/components/hooks/useTypedSegments.js.map +1 -1
  51. package/lib/commonjs/components/theme/defaultStrings.js +13 -3
  52. package/lib/commonjs/components/theme/defaultStrings.js.map +1 -1
  53. package/lib/commonjs/components/theme/direction.js +17 -0
  54. package/lib/commonjs/components/theme/direction.js.map +1 -0
  55. package/lib/commonjs/components/theme/numerals.js +16 -0
  56. package/lib/commonjs/components/theme/numerals.js.map +1 -0
  57. package/lib/commonjs/components/theme/styleKeys.js +10 -2
  58. package/lib/commonjs/components/theme/styleKeys.js.map +1 -1
  59. package/lib/commonjs/components/theme/tokens.js +4 -0
  60. package/lib/commonjs/components/theme/tokens.js.map +1 -1
  61. package/lib/commonjs/components/time/TimeRow.js +10 -2
  62. package/lib/commonjs/components/time/TimeRow.js.map +1 -1
  63. package/lib/commonjs/components/time/TypedTime.js +3 -1
  64. package/lib/commonjs/components/time/TypedTime.js.map +1 -1
  65. package/lib/commonjs/components/time/Wheel.js +20 -3
  66. package/lib/commonjs/components/time/Wheel.js.map +1 -1
  67. package/lib/commonjs/components/time/timeStyles.js +4 -5
  68. package/lib/commonjs/components/time/timeStyles.js.map +1 -1
  69. package/lib/commonjs/components/types.js +10 -0
  70. package/lib/commonjs/components/types.js.map +1 -1
  71. package/lib/commonjs/core/calendarSystem.js +123 -22
  72. package/lib/commonjs/core/calendarSystem.js.map +1 -1
  73. package/lib/commonjs/core/digits.js +25 -0
  74. package/lib/commonjs/core/digits.js.map +1 -0
  75. package/lib/commonjs/core/draft.js +31 -10
  76. package/lib/commonjs/core/draft.js.map +1 -1
  77. package/lib/commonjs/core/locale.js +20 -4
  78. package/lib/commonjs/core/locale.js.map +1 -1
  79. package/lib/commonjs/core/numerals.js +46 -0
  80. package/lib/commonjs/core/numerals.js.map +1 -0
  81. package/lib/commonjs/core/segmentEditor.js +9 -0
  82. package/lib/commonjs/core/segmentEditor.js.map +1 -1
  83. package/lib/commonjs/core/segments.js +2 -11
  84. package/lib/commonjs/core/segments.js.map +1 -1
  85. package/lib/commonjs/core/selectionRules.js +2 -1
  86. package/lib/commonjs/core/selectionRules.js.map +1 -1
  87. package/lib/commonjs/core/timeEntry.js +7 -91
  88. package/lib/commonjs/core/timeEntry.js.map +1 -1
  89. package/lib/commonjs/icons/index.js +5 -1
  90. package/lib/commonjs/icons/index.js.map +1 -1
  91. package/lib/commonjs/index.js +20 -0
  92. package/lib/commonjs/index.js.map +1 -1
  93. package/lib/module/adapters/utcMidnight.js +14 -5
  94. package/lib/module/adapters/utcMidnight.js.map +1 -1
  95. package/lib/module/components/DateTimePicker.js +490 -91
  96. package/lib/module/components/DateTimePicker.js.map +1 -1
  97. package/lib/module/components/calendar/Calendar.js +25 -11
  98. package/lib/module/components/calendar/Calendar.js.map +1 -1
  99. package/lib/module/components/calendar/CalendarHeader.js +47 -7
  100. package/lib/module/components/calendar/CalendarHeader.js.map +1 -1
  101. package/lib/module/components/calendar/CalendarSwitch.js +145 -0
  102. package/lib/module/components/calendar/CalendarSwitch.js.map +1 -0
  103. package/lib/module/components/calendar/DayCell.js +8 -3
  104. package/lib/module/components/calendar/DayCell.js.map +1 -1
  105. package/lib/module/components/calendar/DayGrid.js +14 -3
  106. package/lib/module/components/calendar/DayGrid.js.map +1 -1
  107. package/lib/module/components/calendar/PeriodGrid.js +3 -1
  108. package/lib/module/components/calendar/PeriodGrid.js.map +1 -1
  109. package/lib/module/components/calendar/calendarStyles.js +3 -2
  110. package/lib/module/components/calendar/calendarStyles.js.map +1 -1
  111. package/lib/module/components/calendar/localeNames.js +46 -24
  112. package/lib/module/components/calendar/localeNames.js.map +1 -1
  113. package/lib/module/components/calendar/selection.js +7 -0
  114. package/lib/module/components/calendar/selection.js.map +1 -1
  115. package/lib/module/components/container/DefaultSheet.js +42 -12
  116. package/lib/module/components/container/DefaultSheet.js.map +1 -1
  117. package/lib/module/components/container/PickerContainer.js +4 -0
  118. package/lib/module/components/container/PickerContainer.js.map +1 -1
  119. package/lib/module/components/container/PickerLayout.js +1 -1
  120. package/lib/module/components/container/PickerLayout.js.map +1 -1
  121. package/lib/module/components/container/containerStyles.js +11 -3
  122. package/lib/module/components/container/containerStyles.js.map +1 -1
  123. package/lib/module/components/field/RangeFields.js +5 -1
  124. package/lib/module/components/field/RangeFields.js.map +1 -1
  125. package/lib/module/components/field/SegmentedInput.js +8 -3
  126. package/lib/module/components/field/SegmentedInput.js.map +1 -1
  127. package/lib/module/components/field/ValueField.js +66 -10
  128. package/lib/module/components/field/ValueField.js.map +1 -1
  129. package/lib/module/components/field/fieldStyles.js +17 -0
  130. package/lib/module/components/field/fieldStyles.js.map +1 -1
  131. package/lib/module/components/field/fieldText.js +28 -11
  132. package/lib/module/components/field/fieldText.js.map +1 -1
  133. package/lib/module/components/hooks/useBottomInset.js +27 -0
  134. package/lib/module/components/hooks/useBottomInset.js.map +1 -0
  135. package/lib/module/components/hooks/useKeyboardFold.js +33 -12
  136. package/lib/module/components/hooks/useKeyboardFold.js.map +1 -1
  137. package/lib/module/components/hooks/useOpenPicker.js +39 -17
  138. package/lib/module/components/hooks/useOpenPicker.js.map +1 -1
  139. package/lib/module/components/hooks/useTypedSegments.js +6 -2
  140. package/lib/module/components/hooks/useTypedSegments.js.map +1 -1
  141. package/lib/module/components/theme/defaultStrings.js +11 -2
  142. package/lib/module/components/theme/defaultStrings.js.map +1 -1
  143. package/lib/module/components/theme/direction.js +13 -0
  144. package/lib/module/components/theme/direction.js.map +1 -0
  145. package/lib/module/components/theme/numerals.js +12 -0
  146. package/lib/module/components/theme/numerals.js.map +1 -0
  147. package/lib/module/components/theme/styleKeys.js +10 -2
  148. package/lib/module/components/theme/styleKeys.js.map +1 -1
  149. package/lib/module/components/theme/tokens.js +4 -0
  150. package/lib/module/components/theme/tokens.js.map +1 -1
  151. package/lib/module/components/time/TimeRow.js +11 -3
  152. package/lib/module/components/time/TimeRow.js.map +1 -1
  153. package/lib/module/components/time/TypedTime.js +3 -1
  154. package/lib/module/components/time/TypedTime.js.map +1 -1
  155. package/lib/module/components/time/Wheel.js +18 -2
  156. package/lib/module/components/time/Wheel.js.map +1 -1
  157. package/lib/module/components/time/timeStyles.js +4 -5
  158. package/lib/module/components/time/timeStyles.js.map +1 -1
  159. package/lib/module/components/types.js +10 -0
  160. package/lib/module/components/types.js.map +1 -1
  161. package/lib/module/core/calendarSystem.js +118 -19
  162. package/lib/module/core/calendarSystem.js.map +1 -1
  163. package/lib/module/core/digits.js +20 -0
  164. package/lib/module/core/digits.js.map +1 -0
  165. package/lib/module/core/draft.js +29 -9
  166. package/lib/module/core/draft.js.map +1 -1
  167. package/lib/module/core/locale.js +18 -3
  168. package/lib/module/core/locale.js.map +1 -1
  169. package/lib/module/core/numerals.js +39 -0
  170. package/lib/module/core/numerals.js.map +1 -0
  171. package/lib/module/core/segmentEditor.js +9 -0
  172. package/lib/module/core/segmentEditor.js.map +1 -1
  173. package/lib/module/core/segments.js +0 -8
  174. package/lib/module/core/segments.js.map +1 -1
  175. package/lib/module/core/selectionRules.js +2 -1
  176. package/lib/module/core/selectionRules.js.map +1 -1
  177. package/lib/module/core/timeEntry.js +7 -89
  178. package/lib/module/core/timeEntry.js.map +1 -1
  179. package/lib/module/icons/index.js +5 -1
  180. package/lib/module/icons/index.js.map +1 -1
  181. package/lib/module/index.js +2 -0
  182. package/lib/module/index.js.map +1 -1
  183. package/lib/typescript/commonjs/src/adapters/utcMidnight.d.ts.map +1 -1
  184. package/lib/typescript/commonjs/src/components/DateTimePicker.d.ts.map +1 -1
  185. package/lib/typescript/commonjs/src/components/calendar/Calendar.d.ts +8 -4
  186. package/lib/typescript/commonjs/src/components/calendar/Calendar.d.ts.map +1 -1
  187. package/lib/typescript/commonjs/src/components/calendar/CalendarHeader.d.ts.map +1 -1
  188. package/lib/typescript/commonjs/src/components/calendar/CalendarSwitch.d.ts +26 -0
  189. package/lib/typescript/commonjs/src/components/calendar/CalendarSwitch.d.ts.map +1 -0
  190. package/lib/typescript/commonjs/src/components/calendar/DayCell.d.ts +1 -1
  191. package/lib/typescript/commonjs/src/components/calendar/DayCell.d.ts.map +1 -1
  192. package/lib/typescript/commonjs/src/components/calendar/DayGrid.d.ts +2 -0
  193. package/lib/typescript/commonjs/src/components/calendar/DayGrid.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/components/calendar/PeriodGrid.d.ts.map +1 -1
  195. package/lib/typescript/commonjs/src/components/calendar/calendarStyles.d.ts +1 -1
  196. package/lib/typescript/commonjs/src/components/calendar/calendarStyles.d.ts.map +1 -1
  197. package/lib/typescript/commonjs/src/components/calendar/localeNames.d.ts +8 -3
  198. package/lib/typescript/commonjs/src/components/calendar/localeNames.d.ts.map +1 -1
  199. package/lib/typescript/commonjs/src/components/calendar/selection.d.ts +2 -0
  200. package/lib/typescript/commonjs/src/components/calendar/selection.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/components/container/DefaultSheet.d.ts +10 -2
  202. package/lib/typescript/commonjs/src/components/container/DefaultSheet.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/components/container/PickerContainer.d.ts.map +1 -1
  204. package/lib/typescript/commonjs/src/components/container/containerStyles.d.ts +6 -0
  205. package/lib/typescript/commonjs/src/components/container/containerStyles.d.ts.map +1 -1
  206. package/lib/typescript/commonjs/src/components/field/RangeFields.d.ts +5 -0
  207. package/lib/typescript/commonjs/src/components/field/RangeFields.d.ts.map +1 -1
  208. package/lib/typescript/commonjs/src/components/field/SegmentedInput.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/components/field/ValueField.d.ts +7 -0
  210. package/lib/typescript/commonjs/src/components/field/ValueField.d.ts.map +1 -1
  211. package/lib/typescript/commonjs/src/components/field/fieldStyles.d.ts +15 -0
  212. package/lib/typescript/commonjs/src/components/field/fieldStyles.d.ts.map +1 -1
  213. package/lib/typescript/commonjs/src/components/field/fieldText.d.ts +14 -5
  214. package/lib/typescript/commonjs/src/components/field/fieldText.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/src/components/hooks/useBottomInset.d.ts +8 -0
  216. package/lib/typescript/commonjs/src/components/hooks/useBottomInset.d.ts.map +1 -0
  217. package/lib/typescript/commonjs/src/components/hooks/useKeyboardFold.d.ts +10 -2
  218. package/lib/typescript/commonjs/src/components/hooks/useKeyboardFold.d.ts.map +1 -1
  219. package/lib/typescript/commonjs/src/components/hooks/useOpenPicker.d.ts +2 -1
  220. package/lib/typescript/commonjs/src/components/hooks/useOpenPicker.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/src/components/hooks/useTypedSegments.d.ts +3 -2
  222. package/lib/typescript/commonjs/src/components/hooks/useTypedSegments.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/src/components/theme/defaultStrings.d.ts +13 -2
  224. package/lib/typescript/commonjs/src/components/theme/defaultStrings.d.ts.map +1 -1
  225. package/lib/typescript/commonjs/src/components/theme/direction.d.ts +8 -0
  226. package/lib/typescript/commonjs/src/components/theme/direction.d.ts.map +1 -0
  227. package/lib/typescript/commonjs/src/components/theme/numerals.d.ts +7 -0
  228. package/lib/typescript/commonjs/src/components/theme/numerals.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/src/components/theme/styleKeys.d.ts +1 -1
  230. package/lib/typescript/commonjs/src/components/theme/styleKeys.d.ts.map +1 -1
  231. package/lib/typescript/commonjs/src/components/theme/tokens.d.ts +3 -1
  232. package/lib/typescript/commonjs/src/components/theme/tokens.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/src/components/time/TimeRow.d.ts.map +1 -1
  234. package/lib/typescript/commonjs/src/components/time/TypedTime.d.ts.map +1 -1
  235. package/lib/typescript/commonjs/src/components/time/Wheel.d.ts +5 -0
  236. package/lib/typescript/commonjs/src/components/time/Wheel.d.ts.map +1 -1
  237. package/lib/typescript/commonjs/src/components/time/timeStyles.d.ts +0 -2
  238. package/lib/typescript/commonjs/src/components/time/timeStyles.d.ts.map +1 -1
  239. package/lib/typescript/commonjs/src/components/types.d.ts +84 -4
  240. package/lib/typescript/commonjs/src/components/types.d.ts.map +1 -1
  241. package/lib/typescript/commonjs/src/core/calendarSystem.d.ts +20 -5
  242. package/lib/typescript/commonjs/src/core/calendarSystem.d.ts.map +1 -1
  243. package/lib/typescript/commonjs/src/core/digits.d.ts +3 -0
  244. package/lib/typescript/commonjs/src/core/digits.d.ts.map +1 -0
  245. package/lib/typescript/commonjs/src/core/draft.d.ts +12 -1
  246. package/lib/typescript/commonjs/src/core/draft.d.ts.map +1 -1
  247. package/lib/typescript/commonjs/src/core/locale.d.ts +6 -0
  248. package/lib/typescript/commonjs/src/core/locale.d.ts.map +1 -1
  249. package/lib/typescript/commonjs/src/core/numerals.d.ts +15 -0
  250. package/lib/typescript/commonjs/src/core/numerals.d.ts.map +1 -0
  251. package/lib/typescript/commonjs/src/core/segmentEditor.d.ts.map +1 -1
  252. package/lib/typescript/commonjs/src/core/segments.d.ts +0 -2
  253. package/lib/typescript/commonjs/src/core/segments.d.ts.map +1 -1
  254. package/lib/typescript/commonjs/src/core/selectionRules.d.ts +3 -2
  255. package/lib/typescript/commonjs/src/core/selectionRules.d.ts.map +1 -1
  256. package/lib/typescript/commonjs/src/core/timeEntry.d.ts +4 -24
  257. package/lib/typescript/commonjs/src/core/timeEntry.d.ts.map +1 -1
  258. package/lib/typescript/commonjs/src/icons/index.d.ts.map +1 -1
  259. package/lib/typescript/commonjs/src/index.d.ts +7 -3
  260. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  261. package/lib/typescript/module/src/adapters/utcMidnight.d.ts.map +1 -1
  262. package/lib/typescript/module/src/components/DateTimePicker.d.ts.map +1 -1
  263. package/lib/typescript/module/src/components/calendar/Calendar.d.ts +8 -4
  264. package/lib/typescript/module/src/components/calendar/Calendar.d.ts.map +1 -1
  265. package/lib/typescript/module/src/components/calendar/CalendarHeader.d.ts.map +1 -1
  266. package/lib/typescript/module/src/components/calendar/CalendarSwitch.d.ts +26 -0
  267. package/lib/typescript/module/src/components/calendar/CalendarSwitch.d.ts.map +1 -0
  268. package/lib/typescript/module/src/components/calendar/DayCell.d.ts +1 -1
  269. package/lib/typescript/module/src/components/calendar/DayCell.d.ts.map +1 -1
  270. package/lib/typescript/module/src/components/calendar/DayGrid.d.ts +2 -0
  271. package/lib/typescript/module/src/components/calendar/DayGrid.d.ts.map +1 -1
  272. package/lib/typescript/module/src/components/calendar/PeriodGrid.d.ts.map +1 -1
  273. package/lib/typescript/module/src/components/calendar/calendarStyles.d.ts +1 -1
  274. package/lib/typescript/module/src/components/calendar/calendarStyles.d.ts.map +1 -1
  275. package/lib/typescript/module/src/components/calendar/localeNames.d.ts +8 -3
  276. package/lib/typescript/module/src/components/calendar/localeNames.d.ts.map +1 -1
  277. package/lib/typescript/module/src/components/calendar/selection.d.ts +2 -0
  278. package/lib/typescript/module/src/components/calendar/selection.d.ts.map +1 -1
  279. package/lib/typescript/module/src/components/container/DefaultSheet.d.ts +10 -2
  280. package/lib/typescript/module/src/components/container/DefaultSheet.d.ts.map +1 -1
  281. package/lib/typescript/module/src/components/container/PickerContainer.d.ts.map +1 -1
  282. package/lib/typescript/module/src/components/container/containerStyles.d.ts +6 -0
  283. package/lib/typescript/module/src/components/container/containerStyles.d.ts.map +1 -1
  284. package/lib/typescript/module/src/components/field/RangeFields.d.ts +5 -0
  285. package/lib/typescript/module/src/components/field/RangeFields.d.ts.map +1 -1
  286. package/lib/typescript/module/src/components/field/SegmentedInput.d.ts.map +1 -1
  287. package/lib/typescript/module/src/components/field/ValueField.d.ts +7 -0
  288. package/lib/typescript/module/src/components/field/ValueField.d.ts.map +1 -1
  289. package/lib/typescript/module/src/components/field/fieldStyles.d.ts +15 -0
  290. package/lib/typescript/module/src/components/field/fieldStyles.d.ts.map +1 -1
  291. package/lib/typescript/module/src/components/field/fieldText.d.ts +14 -5
  292. package/lib/typescript/module/src/components/field/fieldText.d.ts.map +1 -1
  293. package/lib/typescript/module/src/components/hooks/useBottomInset.d.ts +8 -0
  294. package/lib/typescript/module/src/components/hooks/useBottomInset.d.ts.map +1 -0
  295. package/lib/typescript/module/src/components/hooks/useKeyboardFold.d.ts +10 -2
  296. package/lib/typescript/module/src/components/hooks/useKeyboardFold.d.ts.map +1 -1
  297. package/lib/typescript/module/src/components/hooks/useOpenPicker.d.ts +2 -1
  298. package/lib/typescript/module/src/components/hooks/useOpenPicker.d.ts.map +1 -1
  299. package/lib/typescript/module/src/components/hooks/useTypedSegments.d.ts +3 -2
  300. package/lib/typescript/module/src/components/hooks/useTypedSegments.d.ts.map +1 -1
  301. package/lib/typescript/module/src/components/theme/defaultStrings.d.ts +13 -2
  302. package/lib/typescript/module/src/components/theme/defaultStrings.d.ts.map +1 -1
  303. package/lib/typescript/module/src/components/theme/direction.d.ts +8 -0
  304. package/lib/typescript/module/src/components/theme/direction.d.ts.map +1 -0
  305. package/lib/typescript/module/src/components/theme/numerals.d.ts +7 -0
  306. package/lib/typescript/module/src/components/theme/numerals.d.ts.map +1 -0
  307. package/lib/typescript/module/src/components/theme/styleKeys.d.ts +1 -1
  308. package/lib/typescript/module/src/components/theme/styleKeys.d.ts.map +1 -1
  309. package/lib/typescript/module/src/components/theme/tokens.d.ts +3 -1
  310. package/lib/typescript/module/src/components/theme/tokens.d.ts.map +1 -1
  311. package/lib/typescript/module/src/components/time/TimeRow.d.ts.map +1 -1
  312. package/lib/typescript/module/src/components/time/TypedTime.d.ts.map +1 -1
  313. package/lib/typescript/module/src/components/time/Wheel.d.ts +5 -0
  314. package/lib/typescript/module/src/components/time/Wheel.d.ts.map +1 -1
  315. package/lib/typescript/module/src/components/time/timeStyles.d.ts +0 -2
  316. package/lib/typescript/module/src/components/time/timeStyles.d.ts.map +1 -1
  317. package/lib/typescript/module/src/components/types.d.ts +84 -4
  318. package/lib/typescript/module/src/components/types.d.ts.map +1 -1
  319. package/lib/typescript/module/src/core/calendarSystem.d.ts +20 -5
  320. package/lib/typescript/module/src/core/calendarSystem.d.ts.map +1 -1
  321. package/lib/typescript/module/src/core/digits.d.ts +3 -0
  322. package/lib/typescript/module/src/core/digits.d.ts.map +1 -0
  323. package/lib/typescript/module/src/core/draft.d.ts +12 -1
  324. package/lib/typescript/module/src/core/draft.d.ts.map +1 -1
  325. package/lib/typescript/module/src/core/locale.d.ts +6 -0
  326. package/lib/typescript/module/src/core/locale.d.ts.map +1 -1
  327. package/lib/typescript/module/src/core/numerals.d.ts +15 -0
  328. package/lib/typescript/module/src/core/numerals.d.ts.map +1 -0
  329. package/lib/typescript/module/src/core/segmentEditor.d.ts.map +1 -1
  330. package/lib/typescript/module/src/core/segments.d.ts +0 -2
  331. package/lib/typescript/module/src/core/segments.d.ts.map +1 -1
  332. package/lib/typescript/module/src/core/selectionRules.d.ts +3 -2
  333. package/lib/typescript/module/src/core/selectionRules.d.ts.map +1 -1
  334. package/lib/typescript/module/src/core/timeEntry.d.ts +4 -24
  335. package/lib/typescript/module/src/core/timeEntry.d.ts.map +1 -1
  336. package/lib/typescript/module/src/icons/index.d.ts.map +1 -1
  337. package/lib/typescript/module/src/index.d.ts +7 -3
  338. package/lib/typescript/module/src/index.d.ts.map +1 -1
  339. package/package.json +22 -7
  340. package/src/adapters/utcMidnight.ts +14 -5
  341. package/src/components/DateTimePicker.tsx +607 -108
  342. package/src/components/calendar/Calendar.tsx +54 -16
  343. package/src/components/calendar/CalendarHeader.tsx +41 -9
  344. package/src/components/calendar/CalendarSwitch.tsx +170 -0
  345. package/src/components/calendar/DayCell.tsx +16 -4
  346. package/src/components/calendar/DayGrid.tsx +18 -3
  347. package/src/components/calendar/PeriodGrid.tsx +3 -1
  348. package/src/components/calendar/calendarStyles.ts +3 -2
  349. package/src/components/calendar/localeNames.ts +52 -22
  350. package/src/components/calendar/selection.ts +8 -0
  351. package/src/components/container/DefaultSheet.tsx +60 -14
  352. package/src/components/container/PickerContainer.tsx +4 -0
  353. package/src/components/container/PickerLayout.tsx +1 -1
  354. package/src/components/container/containerStyles.ts +12 -3
  355. package/src/components/field/RangeFields.tsx +12 -2
  356. package/src/components/field/SegmentedInput.tsx +8 -3
  357. package/src/components/field/ValueField.tsx +94 -18
  358. package/src/components/field/fieldStyles.ts +6 -0
  359. package/src/components/field/fieldText.ts +68 -15
  360. package/src/components/hooks/useBottomInset.ts +33 -0
  361. package/src/components/hooks/useKeyboardFold.ts +41 -13
  362. package/src/components/hooks/useOpenPicker.ts +41 -18
  363. package/src/components/hooks/useTypedSegments.ts +7 -3
  364. package/src/components/theme/defaultStrings.ts +29 -4
  365. package/src/components/theme/direction.ts +12 -0
  366. package/src/components/theme/numerals.ts +11 -0
  367. package/src/components/theme/styleKeys.ts +18 -1
  368. package/src/components/theme/tokens.ts +5 -1
  369. package/src/components/time/TimeRow.tsx +23 -3
  370. package/src/components/time/TypedTime.tsx +3 -1
  371. package/src/components/time/Wheel.tsx +19 -5
  372. package/src/components/time/timeStyles.ts +2 -3
  373. package/src/components/types.ts +86 -4
  374. package/src/core/calendarSystem.ts +145 -21
  375. package/src/core/digits.ts +21 -0
  376. package/src/core/draft.ts +41 -13
  377. package/src/core/locale.ts +20 -3
  378. package/src/core/numerals.ts +35 -0
  379. package/src/core/segmentEditor.ts +13 -0
  380. package/src/core/segments.ts +0 -8
  381. package/src/core/selectionRules.ts +3 -2
  382. package/src/core/timeEntry.ts +6 -96
  383. package/src/icons/index.tsx +3 -1
  384. package/src/index.tsx +12 -2
@@ -2,18 +2,24 @@
2
2
 
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { DateTime } from 'luxon';
5
+ import { View } from 'react-native';
5
6
  import { resolveAdapter } from "../adapters/index.js";
6
- import { daysBetween, isSameDay } from "../core/calendarDate.js";
7
+ import { compareCalendarDates, daysBetween, isSameDay } from "../core/calendarDate.js";
7
8
  import { createConstraints } from "../core/constraints.js";
8
- import { createDraft, DEFAULT_TIME, draftToWire, setTime as applyTime, validateDraft } from "../core/draft.js";
9
- import { resolveCalendarSystem } from "../core/calendarSystem.js";
9
+ import { clearDraft as emptied, createDraft, DEFAULT_TIME, draftToWire, sameDayRangeOk, setTime as applyTime, validateDraft } from "../core/draft.js";
10
+ import { hijriDateText, hijriPeriods, isHijriSupported, resolveCalendarSystem } from "../core/calendarSystem.js";
10
11
  import { defaultLocale, deriveDateFormat, deriveFirstDayOfWeek, deriveTimeFormat, normalizeDateFormat } from "../core/locale.js";
12
+ import { arabicDigits, latinDigits, localeDigits } from "../core/numerals.js";
11
13
  import { withSelectionRules } from "../core/selectionRules.js";
12
14
  import { checkTime } from "../core/timeEntry.js";
13
15
  import Calendar from "./calendar/Calendar.js";
16
+ import CalendarSwitch from "./calendar/CalendarSwitch.js";
14
17
  import { localeNames } from "./calendar/localeNames.js";
18
+ import { RtlContext } from "./theme/direction.js";
19
+ import { NumeralsContext } from "./theme/numerals.js";
20
+ import { firstSelectedDay } from "./calendar/selection.js";
15
21
  import PickerContainer from "./container/PickerContainer.js";
16
- import { formatMultipleText, formatRangeText, formatValueText } from "./field/fieldText.js";
22
+ import { formatDateText, formatMultipleText, formatRangeText, formatValueText } from "./field/fieldText.js";
17
23
  import FieldMessages from "./field/FieldMessages.js";
18
24
  import RangeFields from "./field/RangeFields.js";
19
25
  import ValueField from "./field/ValueField.js";
@@ -22,17 +28,43 @@ import { useOpenPicker } from "./hooks/useOpenPicker.js";
22
28
  import { usePickerDraft } from "./hooks/usePickerDraft.js";
23
29
  import { useTodayTick } from "./hooks/useTodayTick.js";
24
30
  import TimeRows from "./time/TimeRows.js";
25
- import { resolveStrings } from "./theme/defaultStrings.js";
31
+ import { DEFAULT_STRINGS, compactStrings, resolveStrings } from "./theme/defaultStrings.js";
26
32
  import { buildTokens } from "./theme/tokens.js";
27
33
  import { DEFAULT_YEAR_RANGE } from "./types.js";
28
34
 
29
35
  /** Same day and time (or both without a time). */
30
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
36
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
31
37
  const sameValue = (a, b) => isSameDay(a.date, b.date) && (a.time?.hour ?? -1) === (b.time?.hour ?? -1) && (a.time?.minute ?? -1) === (b.time?.minute ?? -1);
38
+ const ALL_SYSTEMS = ['gregory', 'hijri', 'dual'];
39
+ const RTL = {
40
+ direction: 'rtl'
41
+ };
42
+
43
+ /** The same kind of selection with nothing in it. */
44
+ const emptySelection = sel => sel.kind === 'single' ? {
45
+ kind: 'single',
46
+ day: null
47
+ } : sel.kind === 'range' ? {
48
+ kind: 'range',
49
+ from: null,
50
+ to: null
51
+ } : {
52
+ ...sel,
53
+ days: []
54
+ };
32
55
 
33
56
  /** Minute steps that divide the hour. */
34
57
  const MINUTE_STEPS = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60];
35
58
 
59
+ /** A folded calendar stays mounted, so it comes back on the month and view it showed. */
60
+ const FOLDED = {
61
+ display: 'none'
62
+ };
63
+
64
+ /** The later / earlier of a limit and a bound; the bound when there's no limit. */
65
+ const later = (a, b) => a && compareCalendarDates(a, b) > 0 ? a : b;
66
+ const earlier = (a, b) => a && compareCalendarDates(a, b) < 0 ? a : b;
67
+
36
68
  /**
37
69
  * A date & time picker: a value field that opens a sheet (or an inline panel)
38
70
  * with a calendar and, for date-time values, a time row. Single, range and
@@ -72,6 +104,7 @@ const DateTimePicker = props => {
72
104
  disabled = false,
73
105
  readOnly = false,
74
106
  allowTyping,
107
+ hideCalendarOnKeyboard = true,
75
108
  allowFieldTyping,
76
109
  allowClear,
77
110
  errorMessage,
@@ -103,6 +136,11 @@ const DateTimePicker = props => {
103
136
  initialVisibleDate,
104
137
  enableMonthSwipe = true,
105
138
  calendarSystem,
139
+ calendarSwitch,
140
+ onCalendarSystemChange,
141
+ dualDefaultTab = 'gregory',
142
+ hijriLocale = 'ar-SA',
143
+ renderCalendarSwitch,
106
144
  showToday = false,
107
145
  showHolidays = true,
108
146
  onVisibleMonthChange,
@@ -110,6 +148,7 @@ const DateTimePicker = props => {
110
148
  validate,
111
149
  yearRange: yearRangeProp = DEFAULT_YEAR_RANGE,
112
150
  locale,
151
+ numerals: numeralsProp,
113
152
  timeZone,
114
153
  now,
115
154
  timeFormat: timeFormatProp,
@@ -129,6 +168,7 @@ const DateTimePicker = props => {
129
168
  theme,
130
169
  styles,
131
170
  strings: stringOverrides,
171
+ hijriStrings: hijriOverrides,
132
172
  testID
133
173
  } = props;
134
174
  const tokens = useMemo(() => buildTokens(theme),
@@ -139,6 +179,69 @@ const DateTimePicker = props => {
139
179
  const firstDayOfWeek = firstDayProp ?? deriveFirstDayOfWeek(loc);
140
180
  // Month and year picks are for single dates only.
141
181
  const precision = type === 'date' && mode === 'single' ? precisionProp : 'day';
182
+ // The calendars the switch offers: none for month and year picks (always
183
+ // Gregorian), or when the platform has no Hijri calendar.
184
+ const switchKey = Array.isArray(calendarSwitch) ? calendarSwitch.join(',') : String(!!calendarSwitch);
185
+ const switchSystems = useMemo(() => {
186
+ if (!calendarSwitch || precision !== 'day' || !isHijriSupported()) return null;
187
+ const list = calendarSwitch === true ? ALL_SYSTEMS : ALL_SYSTEMS.filter(x => calendarSwitch.includes(x)).sort((a, b) => calendarSwitch.indexOf(a) - calendarSwitch.indexOf(b));
188
+ return list.length > 1 ? list : null;
189
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- the list is compared by content
190
+ }, [switchKey, precision]);
191
+ const startSystem = () => {
192
+ const wanted = calendarSystem ?? 'gregory';
193
+ return switchSystems && !switchSystems.includes(wanted) ? switchSystems[0] ?? wanted : wanted;
194
+ };
195
+ // The switch changes this; a new calendarSystem prop starts over from it.
196
+ const [chosenSystem, setChosenSystem] = useState(startSystem);
197
+ // The calendar the props open on, which a value from outside counts as
198
+ // picked in. The switch's own choice coming back as the calendarSystem prop
199
+ // (onCalendarSystemChange={setSystem}) isn't a new one.
200
+ const [openedOn, setOpenedOn] = useState(startSystem);
201
+ const echo = useRef(null);
202
+ useEffect(() => {
203
+ const next = startSystem();
204
+ const own = echo.current === next;
205
+ echo.current = null;
206
+ if (own) return;
207
+ setChosenSystem(next);
208
+ setOpenedOn(next);
209
+ },
210
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- only when the props change
211
+ [calendarSystem, switchKey]);
212
+ // Month and year picks are Gregorian whatever the calendar system.
213
+ const system = precision === 'day' ? resolveCalendarSystem(chosenSystem) : 'gregory';
214
+ // A dual calendar shows one of its two tabs; each open starts on dualDefaultTab.
215
+ const [dualTab, setDualTab] = useState(dualDefaultTab);
216
+ /** The calendar on screen. */
217
+ const shownSystem = system === 'dual' ? dualTab : system;
218
+ // A value from outside counts as picked in the calendar the props open on,
219
+ // not the one the user has since chosen in the switch.
220
+ const propSystem = precision === 'day' ? resolveCalendarSystem(openedOn) : 'gregory';
221
+ const outsideSystem = propSystem === 'dual' ? dualDefaultTab : propSystem;
222
+ // Picks say which calendar they were made in only when one can be Hijri.
223
+ const hijriInPlay = switchSystems !== null || system !== 'gregory';
224
+ /**
225
+ * The digits for one calendar: a Hijri calendar (and a date picked in it)
226
+ * shows Arabic digits (١٢٣) in any language; a Gregorian one shows 0–9.
227
+ * `numerals` sets both.
228
+ */
229
+ const digitsFor = sys => {
230
+ const kind = numeralsProp ?? (sys === 'hijri' ? 'arabic' : 'latin');
231
+ if (kind === 'arabic') return arabicDigits;
232
+ if (kind === 'latin') return latinDigits;
233
+ return localeDigits(sys === 'hijri' ? hijriLocale : loc);
234
+ };
235
+ /**
236
+ * The strings for one calendar. A Hijri one takes `hijriStrings` first, then
237
+ * AM / PM in `hijriLocale` (ص / م), then `strings`.
238
+ */
239
+ const hijriStrings = useMemo(() => ({
240
+ ...strings,
241
+ ...hijriPeriods(hijriLocale),
242
+ ...compactStrings(hijriOverrides)
243
+ }), [strings, hijriLocale, hijriOverrides]);
244
+ const stringsFor = sys => sys === 'hijri' ? hijriStrings : strings;
142
245
  const monthNames = useMemo(() => localeNames(loc).monthsLong, [loc]);
143
246
  /** A month or year pick shown as text: "March 2026" or "2026". */
144
247
  const periodText = v => {
@@ -202,7 +305,7 @@ const DateTimePicker = props => {
202
305
  const {
203
306
  isOpen: sheetOpen,
204
307
  open,
205
- close
308
+ close: closePicker
206
309
  } = useOpenPicker({
207
310
  disabled,
208
311
  readOnly,
@@ -212,7 +315,7 @@ const DateTimePicker = props => {
212
315
  onClose
213
316
  });
214
317
  const isOpen = isInline || sheetOpen;
215
- const keyboardUp = useKeyboardFold(isOpen);
318
+ const keyboardUp = useKeyboardFold(isOpen && hideCalendarOnKeyboard);
216
319
  // The picker's own boxes that have focus. Inline, another field on the
217
320
  // screen can raise the keyboard, so the calendar folds only for these.
218
321
  const [focusedParts, setFocusedParts] = useState({});
@@ -259,16 +362,36 @@ const DateTimePicker = props => {
259
362
  };
260
363
  })).catch(() => requestedYears.current.delete(year));
261
364
  }, [loadHolidays]);
365
+ // A month's grid also shows days of the months either side, and a Hijri
366
+ // month can run from December into January, so a month at either end of a
367
+ // year loads the year next to it too.
368
+ const loadHolidaysAround = useCallback((year, month) => {
369
+ loadHolidaysFor(year);
370
+ if (month >= 11 && year < yearRange.last) loadHolidaysFor(year + 1);
371
+ if (month === 1 && year > yearRange.first) loadHolidaysFor(year - 1);
372
+ }, [loadHolidaysFor, yearRange.first, yearRange.last]);
262
373
  const holidays = useMemo(() => [...(holidaysProp ?? []), ...Object.values(loadedHolidays).flat()], [holidaysProp, loadedHolidays]);
263
374
 
264
375
  // Limits are worked out when the picker opens (the screen's own validation
265
376
  // after Confirm stays the final check).
266
377
  const weekdaysKey = disabledWeekdays?.join(',');
378
+ // Limits built with `dependsOnNow` ("from now on") are rebuilt every minute
379
+ // while the picker is open.
380
+ const [minuteTick, setMinuteTick] = useState(0);
267
381
  const constraints = useMemo(() => {
268
382
  const nowDate = now?.() ?? new Date();
383
+ // yearRange bounds the limits too, so days outside it are greyed.
269
384
  const limits = {
270
- minDate,
271
- maxDate,
385
+ minDate: later(minDate, {
386
+ year: yearRange.first,
387
+ month: 1,
388
+ day: 1
389
+ }),
390
+ maxDate: earlier(maxDate, {
391
+ year: yearRange.last,
392
+ month: 12,
393
+ day: 31
394
+ }),
272
395
  disabledDates,
273
396
  enabledDates,
274
397
  timeBounds,
@@ -291,8 +414,8 @@ const DateTimePicker = props => {
291
414
  type,
292
415
  timeZone: zone,
293
416
  now: nowDate,
294
- minDate,
295
- maxDate,
417
+ minDate: limits.minDate,
418
+ maxDate: limits.maxDate,
296
419
  isDisabled: disabledDates,
297
420
  isEnabled: enabledDates,
298
421
  timeBounds,
@@ -300,8 +423,25 @@ const DateTimePicker = props => {
300
423
  holidays,
301
424
  disableHolidays
302
425
  });
303
- // eslint-disable-next-line react-hooks/exhaustive-deps -- `isOpen` and `today` refresh the limits on open and at midnight; weekdays are compared by content
304
- }, [isOpen, today, type, zone, now, holidays, minDate, maxDate, disabledDates, enabledDates, timeBounds, weekdaysKey, disableHolidays, getConstraints]);
426
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `isOpen`, `today` and `minuteTick` refresh the limits on open, at midnight and each minute; weekdays are compared by content
427
+ }, [isOpen, today, minuteTick, type, zone, now, holidays, minDate, maxDate, disabledDates, enabledDates, timeBounds, weekdaysKey, disableHolidays, getConstraints, yearRange]);
428
+ const followsClock = isOpen && constraints.dependsOnNow;
429
+ useEffect(() => {
430
+ if (!followsClock) return;
431
+ let timer;
432
+ const arm = () => {
433
+ const ms = 60_000 - (now?.() ?? new Date()).getTime() % 60_000;
434
+ timer = setTimeout(() => {
435
+ setMinuteTick(n => n + 1);
436
+ arm();
437
+ }, ms);
438
+ // Under Node (e.g. Jest) the timer must not keep the process alive.
439
+ timer.unref?.();
440
+ };
441
+ arm();
442
+ return () => clearTimeout(timer);
443
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- armed while open with a clock-bound limit
444
+ }, [followsClock]);
305
445
 
306
446
  // A typed field that doesn't parse blocks Confirm, per field: one end of a
307
447
  // range can't hide the other end's problem.
@@ -328,14 +468,30 @@ const DateTimePicker = props => {
328
468
  const [wheelSpinning, setWheelSpinning] = useState(false);
329
469
  // From/To layout: the end that calendar taps fill. perDate: the day the time row edits.
330
470
  const [activeEnd, setActiveEnd] = useState('from');
471
+ // From/To layout: a From or To field was tapped, so the next day tap edits that end only.
472
+ const [endPicked, setEndPicked] = useState(false);
473
+ const activateEnd = useCallback(end => {
474
+ setActiveEnd(end);
475
+ setEndPicked(true);
476
+ }, []);
331
477
  const [activeDay, setActiveDay] = useState(null);
332
478
  const resetSelectionUi = useCallback(() => {
479
+ // What was typed is gone with the selection, so nothing blocks Confirm.
480
+ setTypingBlocks({});
333
481
  setPendingTimes({});
334
482
  setActiveEnd('from');
483
+ setEndPicked(false);
335
484
  setActiveDay(null);
336
485
  }, []);
486
+ // A day in the month the calendar last showed, so a switch to the other
487
+ // calendar opens on the same days. Each open starts from the value again.
488
+ const shownAnchor = useRef(null);
489
+ const rememberAnchor = useCallback(day => {
490
+ shownAnchor.current = day;
491
+ }, []);
337
492
  // Opening or closing starts fresh: nothing edited, nothing blocked.
338
493
  useEffect(() => {
494
+ shownAnchor.current = null;
339
495
  edited.current = false;
340
496
  setTypingBlocks({});
341
497
  setTimeCheck(0);
@@ -399,6 +555,59 @@ const DateTimePicker = props => {
399
555
  };
400
556
  const valueKey = JSON.stringify(value ?? '');
401
557
 
558
+ // A selection belongs to the calendar it was picked in (Gregorian or Hijri):
559
+ // it shows only there, and the field writes it in that calendar. A saved
560
+ // value remembers its calendar; one from outside counts as picked in the
561
+ // calendar the picker opens on.
562
+ // The saved value as a draft, for the on-screen field and for comparing
563
+ // values by what they hold.
564
+ const saved = useMemo(() => createDraft(mode, value, draftOptions),
565
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- the value is compared by content (`valueKey`)
566
+ [valueKey, mode, draftOptions]);
567
+ /** A draft's days and times, so a value written another way ({to, from}) still matches. */
568
+ const contentOf = dr => JSON.stringify(valuesOf(dr));
569
+ const savedContent = contentOf(saved);
570
+ const savedSystemRef = useRef(null);
571
+ const savedSystem = savedSystemRef.current?.content === savedContent ? savedSystemRef.current.system : outsideSystem;
572
+ const [draftSystem, setDraftSystem] = useState(savedSystem);
573
+ // The draft is rebuilt on open and on a new value; so is its calendar. A new
574
+ // calendarSystem prop while the user is picking starts the selection over,
575
+ // since a selection is one calendar's.
576
+ const systemSeen = useRef({
577
+ isOpen,
578
+ valueKey,
579
+ savedSystem
580
+ });
581
+ useEffect(() => {
582
+ const seen = systemSeen.current;
583
+ systemSeen.current = {
584
+ isOpen,
585
+ valueKey,
586
+ savedSystem
587
+ };
588
+ setDraftSystem(savedSystem);
589
+ const systemOnly = seen.isOpen === isOpen && seen.valueKey === valueKey && seen.savedSystem !== savedSystem;
590
+ if (systemOnly && isOpen && edited.current) {
591
+ draft.clear();
592
+ resetSelectionUi();
593
+ }
594
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- the draft's actions are stable
595
+ }, [isOpen, valueKey, savedSystem]);
596
+ useEffect(() => setDualTab(dualDefaultTab), [isOpen, dualDefaultTab]);
597
+ const selectionShown = draftSystem === shownSystem;
598
+ /** Saves a value: with the calendar it was picked in, when one can be Hijri. */
599
+ const emitChange = useCallback((wire, picked) => {
600
+ savedSystemRef.current = {
601
+ content: contentOf(createDraft(mode, wire, draftOptions)),
602
+ system: picked
603
+ };
604
+ if (hijriInPlay) onChange(wire, {
605
+ calendarSystem: picked
606
+ });else onChange(wire);
607
+ },
608
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- contentOf reads only `type` and the options listed
609
+ [hijriInPlay, onChange, mode, draftOptions]);
610
+
402
611
  /**
403
612
  * Whether a draft can be saved as it stands: complete, every day allowed,
404
613
  * every time allowed and on the minute step, and nothing being typed or
@@ -411,19 +620,19 @@ const DateTimePicker = props => {
411
620
 
412
621
  // Every save: Confirm, an 'instant' edit (onConfirm is added by `confirm`).
413
622
  const commit = useCallback(wire => {
414
- onChange(wire);
623
+ emitChange(wire, draftSystem);
415
624
  onBlur?.();
416
- }, [onChange, onBlur]);
625
+ }, [emitChange, draftSystem, onBlur]);
417
626
  const confirm = useCallback(() => {
418
627
  if (!canConfirm) return;
419
628
  const {
420
629
  validate: check,
421
630
  onError: report,
422
- value: saved
631
+ value: current
423
632
  } = latest.current;
424
633
  if (timeBlocked) {
425
634
  setTimeCheck(n => n + 1);
426
- report?.(timeIssue ?? 'TIME_NOT_ALLOWED', saved);
635
+ report?.(timeIssue ?? 'TIME_NOT_ALLOWED', current);
427
636
  return;
428
637
  }
429
638
  const wire = draftToWire(preview, draftOptions);
@@ -439,14 +648,14 @@ const DateTimePicker = props => {
439
648
  commit(wire);
440
649
  onConfirm?.(wire);
441
650
  edited.current = false;
442
- if (!isInline) close();
443
- }, [canConfirm, timeBlocked, timeIssue, commit, onConfirm, preview, draftOptions, isInline, close]);
651
+ if (!isInline) closePicker();
652
+ }, [canConfirm, timeBlocked, timeIssue, commit, onConfirm, preview, draftOptions, isInline, closePicker]);
444
653
  const clearValue = useCallback(() => {
445
654
  draft.clear();
446
655
  resetSelectionUi();
447
656
  edited.current = false;
448
- onChange('');
449
- }, [draft, onChange, resetSelectionUi]);
657
+ emitChange('', draftSystem);
658
+ }, [draft, emitChange, draftSystem, resetSelectionUi]);
450
659
  const clearDraft = useCallback(() => {
451
660
  edited.current = true;
452
661
  draft.clear();
@@ -455,9 +664,10 @@ const DateTimePicker = props => {
455
664
 
456
665
  // × in the field above the calendar. 'instant' saves every edit, so it
457
666
  // clears the saved value too (once; an empty value isn't saved again).
667
+ const hasSaved = valuesOf(saved).some(v => v !== null);
458
668
  const clearInSheet = useCallback(() => {
459
- if (commitMode === 'instant' && valueKey !== '""') clearValue();else clearDraft();
460
- }, [commitMode, valueKey, clearValue, clearDraft]);
669
+ if (commitMode === 'instant' && hasSaved) clearValue();else clearDraft();
670
+ }, [commitMode, hasSaved, clearValue, clearDraft]);
461
671
 
462
672
  // Opening or closing starts without the last `validate` message.
463
673
  useEffect(() => setValidation(null), [isOpen]);
@@ -468,15 +678,27 @@ const DateTimePicker = props => {
468
678
  // An edit equal to the saved value isn't saved again; one `validate`
469
679
  // rejected waits until the selection or the saved value changes.
470
680
  const lastRejected = useRef(null);
681
+ // The last value saved: not saved again while the parent's `value` catches
682
+ // up (a slow store, a server round trip) or when it never does.
683
+ const lastEmitted = useRef(null);
471
684
  useEffect(() => {
472
685
  lastRejected.current = null;
473
- }, [valueKey]);
474
- useEffect(() => {
686
+ lastEmitted.current = null;
687
+ }, [valueKey, isOpen]);
688
+ // The field in the sheet saves once typing there is done (focus leaves it),
689
+ // so 09:30 isn't saved as 09:00 and 09:03 on the way.
690
+ const sheetTyping = !!(focusedParts.value || focusedParts['value-from'] || focusedParts['value-to']);
691
+ /** Saves the draft in instant mode; `typingDone` once the sheet's field is left. */
692
+ const saveInstant = typingDone => {
475
693
  if (commitMode !== 'instant' || !isOpen || !edited.current) return;
476
- if (timeIssue || !canSave(draft.draft)) return;
694
+ if (!typingDone || timeIssue || !canSave(draft.draft)) return;
477
695
  const wire = draft.toWire();
478
696
  const key = JSON.stringify(wire);
479
- if (key === valueKey || key === lastRejected.current) return;
697
+ if (key === lastRejected.current || key === lastEmitted.current) return;
698
+ // The same dates picked in the same calendar aren't saved again; the same
699
+ // dates picked in the other calendar are.
700
+ const unchanged = key === valueKey || contentOf(draft.draft) === savedContent;
701
+ if (unchanged && draftSystem === savedSystem) return;
480
702
  const message = latest.current.validate?.(wire);
481
703
  if (message) {
482
704
  lastRejected.current = key;
@@ -487,9 +709,22 @@ const DateTimePicker = props => {
487
709
  latest.current.onError?.('VALIDATION', wire);
488
710
  return;
489
711
  }
712
+ lastEmitted.current = key;
490
713
  commit(wire);
491
- // eslint-disable-next-line react-hooks/exhaustive-deps -- when the draft or what blocks it changes, not when callbacks are re-created
492
- }, [commitMode, isOpen, draft.draft, timeIssue, typingBlocked, wheelSpinning, confirmDisabled, constraints]);
714
+ };
715
+ // Only typing waits for the field to be left; a tap is a finished edit.
716
+ const typedLast = useRef(false);
717
+ useEffect(() => saveInstant(!sheetTyping || !typedLast.current),
718
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- when the draft or what blocks it changes, not when callbacks are re-created
719
+ [commitMode, isOpen, sheetTyping, draft.draft, draftSystem, timeIssue, typingBlocked, wheelSpinning, confirmDisabled, constraints]);
720
+ // Closing while the sheet's field still has focus ends the typing: an
721
+ // instant picker saves what was typed before it closes.
722
+ const saveInstantRef = useRef(saveInstant);
723
+ saveInstantRef.current = saveInstant;
724
+ const close = useCallback(() => {
725
+ saveInstantRef.current(true);
726
+ closePicker();
727
+ }, [closePicker]);
493
728
 
494
729
  // A typed date with no time yet gets one the way a tapped day does: the fixed
495
730
  // newDayTime, else a time already typed in the time row, else now.
@@ -497,14 +732,24 @@ const DateTimePicker = props => {
497
732
  date: parsed.dateOnly,
498
733
  time: fixedTime ?? pending[key] ?? nowTime()
499
734
  } : null);
735
+
736
+ // Typed segments are Gregorian, so typing keeps the calendar a selection was
737
+ // picked in; into an empty draft, it takes the calendar on screen.
738
+ const adoptShownIfEmpty = () => {
739
+ if (valuesOf(d).every(v => v === null)) setDraftSystem(shownSystem);
740
+ };
500
741
  const onSheetTyped = parsed => {
501
742
  edited.current = true;
743
+ typedLast.current = true;
744
+ adoptShownIfEmpty();
502
745
  const typed = withFilledTime(parsed, 'single');
503
746
  setTypingBlock('single', !parsed.isEmpty && !typed);
504
747
  if (typed) draft.setValue(typed, 'single');else if (parsed.isEmpty) draft.clear();
505
748
  };
506
749
  const onRangeTyped = (end, parsed) => {
507
750
  edited.current = true;
751
+ typedLast.current = true;
752
+ adoptShownIfEmpty();
508
753
  const typed = withFilledTime(parsed, end);
509
754
  setTypingBlock(end, !parsed.isEmpty && !typed);
510
755
  if (typed || parsed.isEmpty) draft.setValue(typed, end);
@@ -525,28 +770,77 @@ const DateTimePicker = props => {
525
770
  minute: capped % 60
526
771
  };
527
772
  }, [now, zone, minuteStep]);
773
+
774
+ // A range end picked on the start's day starts an hour after the start (at
775
+ // most the day's latest allowed time, or its last step), so the range isn't
776
+ // out of order before the user has touched the time.
777
+ // A time typed for the end before its day is kept as typed.
778
+ const endTimeFor = (from, day, typed) => {
779
+ if (!fixedTime && typed) return typed;
780
+ const time = fixedTime ?? nowTime();
781
+ if (!from?.time || !isSameDay(from.date, day)) return time;
782
+ const start = from.time.hour * 60 + from.time.minute;
783
+ if (time.hour * 60 + time.minute > start) return time;
784
+ // Within the day's allowed hours when it has them.
785
+ const max = constraints.timeBounds(day)?.max;
786
+ const last = max ? max.hour * 60 + max.minute : 24 * 60 - Math.max(1, minuteStep);
787
+ const end = Math.min(start + 60, last);
788
+ return {
789
+ hour: Math.floor(end / 60),
790
+ minute: end % 60
791
+ };
792
+ };
528
793
  const onSelectDay = useCallback(day => {
529
794
  edited.current = true;
530
- if (d.mode === 'range') {
795
+ typedLast.current = false;
796
+ // A pick in the other calendar starts over: the selection is one calendar's.
797
+ const fresh = draftSystem !== shownSystem && valuesOf(d).some(v => v !== null);
798
+ setDraftSystem(shownSystem);
799
+ const cur = fresh ? emptied(d) : d;
800
+ const end0 = fresh ? 'from' : activeEnd;
801
+ const dayInEdit = fresh ? null : activeDay;
802
+ const times = fresh ? {} : pending;
803
+ if (fresh) {
804
+ draft.clear();
805
+ resetSelectionUi();
806
+ }
807
+ if (cur.mode === 'range') {
531
808
  if (rangeLayout === 'fromTo') {
809
+ setEndPicked(false);
810
+ // A full range: the tap starts a new one (or moves the nearest end).
811
+ if (!endPicked && cur.from.value && cur.to.value) {
812
+ const moveNearest = range?.reselect === 'moveNearestEnd';
813
+ const nearest = Math.abs(daysBetween(cur.from.value.date, day)) < Math.abs(daysBetween(cur.to.value.date, day)) ? 'from' : 'to';
814
+ const end = moveNearest ? nearest : 'from';
815
+ setTypingBlock('from', false);
816
+ setTypingBlock('to', false);
817
+ draft.selectDay(day, fixedTime ?? times[end] ?? nowTime());
818
+ setActiveEnd(moveNearest ? nearest : 'to');
819
+ return;
820
+ }
821
+ // Choosing the end after a start tap: a day before the start moves the start.
822
+ const end1 = end0 === 'to' && !endPicked && cur.from.value && compareCalendarDates(day, cur.from.value.date) < 0 ? 'from' : end0;
532
823
  // The tapped day replaces whatever was typed in this end only.
533
- setTypingBlock(activeEnd, false);
534
- draft.setRangeEnd(day, activeEnd, fixedTime ?? pending[activeEnd] ?? nowTime());
535
- if (activeEnd === 'from') setActiveEnd('to');
824
+ setTypingBlock(end1, false);
825
+ draft.setRangeEnd(day, end1, end1 === 'to' ? endTimeFor(cur.from.value, day, times.to) : fixedTime ?? times.from ?? nowTime());
826
+ // The start tapped again where a range can't be one day deselects
827
+ // it, so the next tap picks the start again.
828
+ const deselects = end1 === 'to' && !sameDayRangeOk(draftOptions) && isSameDay(cur.from.value?.date, day);
829
+ if (end0 === 'from') setActiveEnd('to');else if (deselects) setActiveEnd('from');
536
830
  return;
537
831
  }
538
832
  // The single field: the first tap is the start, the next one the end.
539
- const startsOver = !d.from.value || !!d.to.value && range?.reselect !== 'moveNearestEnd';
833
+ const startsOver = !cur.from.value || !!cur.to.value && range?.reselect !== 'moveNearestEnd';
540
834
  const end = startsOver ? 'from' : 'to';
541
835
  setTypingBlock('single', false);
542
- draft.selectDay(day, fixedTime ?? pending[end] ?? nowTime());
836
+ draft.selectDay(day, end === 'to' && !cur.to.value ? endTimeFor(cur.from.value, day, times.to) : fixedTime ?? times[end] ?? nowTime());
543
837
  return;
544
838
  }
545
- if (d.mode === 'multiple') {
839
+ if (cur.mode === 'multiple') {
546
840
  const perDate = type === 'datetime' && multipleTimeMode === 'perDate';
547
- const picked = d.slots.some(slot => isSameDay(slot.value?.date, day));
841
+ const picked = cur.slots.some(slot => isSameDay(slot.value?.date, day));
548
842
  // perDate: a tap on a picked day first makes it the one the time row edits.
549
- if (perDate && picked && !isSameDay(activeDay, day)) {
843
+ if (perDate && picked && !isSameDay(dayInEdit, day)) {
550
844
  setActiveDay(day);
551
845
  return;
552
846
  }
@@ -555,18 +849,28 @@ const DateTimePicker = props => {
555
849
  return;
556
850
  }
557
851
  setTypingBlock('single', false);
558
- draft.selectDay(day, fixedTime ?? pending.single ?? nowTime());
559
- }, [d, draft, nowTime, rangeLayout, activeEnd, activeDay, fixedTime, pending, range?.reselect, type, multipleTimeMode, setTypingBlock]);
852
+ draft.selectDay(day, fixedTime ?? times.single ?? nowTime());
853
+ },
854
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- valuesOf reads only `d` and props listed here
855
+ [d, draft, nowTime, rangeLayout, activeEnd, activeDay, fixedTime, pending, range?.reselect, type, multipleTimeMode, setTypingBlock, draftSystem, shownSystem, resetSelectionUi, endPicked, draftOptions, minuteStep, constraints]);
856
+
857
+ // The calendar gets a tap handler that never changes, so its memoized day
858
+ // cells don't all redraw on every tap; it calls the current one.
859
+ const selectDayRef = useRef(onSelectDay);
860
+ selectDayRef.current = onSelectDay;
861
+ const onSelectDayStable = useCallback(day => selectDayRef.current(day), []);
560
862
 
561
863
  // Greying that depends on what's picked: range length, From/To order, the multiple cap.
562
864
  const calendarConstraints = useMemo(() => {
563
865
  if (d.mode === 'range') {
564
- const choosingEnd = rangeLayout === 'fromTo' ? activeEnd === 'to' : !!d.from.value && !d.to.value;
866
+ // A full From/To range greys nothing: the next tap starts a new one.
867
+ const choosingEnd = rangeLayout === 'fromTo' ? activeEnd === 'to' && (endPicked || !d.to.value) : !!d.from.value && !d.to.value;
565
868
  return withSelectionRules(constraints, {
566
869
  rangeStart: choosingEnd ? d.from.value?.date : null,
567
870
  minDays: range?.minDays,
568
871
  maxDays: range?.maxDays,
569
- blockBeforeStart: rangeLayout === 'fromTo'
872
+ // Only an end chosen from the To field can't go before the start.
873
+ blockBeforeStart: rangeLayout === 'fromTo' && endPicked
570
874
  });
571
875
  }
572
876
  if (d.mode === 'multiple') {
@@ -577,7 +881,7 @@ const DateTimePicker = props => {
577
881
  });
578
882
  }
579
883
  return constraints;
580
- }, [constraints, d, rangeLayout, activeEnd, range?.minDays, range?.maxDays, multiple?.maxSelections]);
884
+ }, [constraints, d, rangeLayout, activeEnd, endPicked, range?.minDays, range?.maxDays, multiple?.maxSelections]);
581
885
  const selection = draft.draft.mode === 'single' ? {
582
886
  kind: 'single',
583
887
  day: draft.draft.slot.value?.date ?? null
@@ -604,13 +908,13 @@ const DateTimePicker = props => {
604
908
  }
605
909
  }, [isOpen, constraints, today]);
606
910
  const handleVisibleMonth = useCallback((year, month) => {
607
- loadHolidaysFor(year);
911
+ loadHolidaysAround(year, month);
608
912
  onVisibleMonthChange?.(year, month);
609
- }, [loadHolidaysFor, onVisibleMonthChange]);
913
+ }, [loadHolidaysAround, onVisibleMonthChange]);
610
914
  useEffect(() => {
611
915
  if (!isOpen) return;
612
- const anchor = selection.kind === 'single' ? selection.day : null;
613
- loadHolidaysFor((anchor ?? today).year);
916
+ const anchor = (selection.kind === 'single' ? selection.day : null) ?? today;
917
+ loadHolidaysAround(anchor.year, anchor.month);
614
918
  // eslint-disable-next-line react-hooks/exhaustive-deps -- once per open
615
919
  }, [isOpen]);
616
920
 
@@ -661,17 +965,24 @@ const DateTimePicker = props => {
661
965
  return;
662
966
  }
663
967
  setTriggerError('');
664
- onChange(wire);
968
+ // Typing keeps the calendar the saved value was picked in.
969
+ emitChange(wire, savedSystem);
665
970
  onBlur?.();
666
971
  };
667
972
 
973
+ /** A value's date in the calendar it was picked in: Hijri text, or none for the dateFormat. */
974
+ const dateTextFor = picked => picked === 'hijri' ? v => hijriDateText(v.date, hijriLocale) ?? formatDateText(v, dateFormat) : undefined;
975
+ const savedDateText = dateTextFor(savedSystem);
976
+ const draftDateText = dateTextFor(draftSystem);
977
+ const savedStrings = stringsFor(savedSystem);
978
+ const draftStrings = stringsFor(draftSystem);
979
+ // Error messages inside the picker follow the tab on screen; the on-screen
980
+ // field's keep `strings`.
981
+ const shownStrings = stringsFor(shownSystem);
982
+
668
983
  // What the on-screen field shows: the saved value, never the draft.
669
- const savedKey = valueKey;
670
- const saved = useMemo(() => createDraft(mode, value, draftOptions),
671
- // eslint-disable-next-line react-hooks/exhaustive-deps -- the value is compared by content (`savedKey`)
672
- [savedKey, mode, draftOptions]);
673
984
  const savedSingle = saved.mode === 'single' ? saved.slot.value : null;
674
- const savedText = saved.mode === 'range' ? formatRangeText(saved.from.value, saved.to.value, type, dateFormat, timeFormat, strings.rangeSeparator) : saved.mode === 'multiple' ? formatMultipleText(saved.slots.map(slot => slot.value), type, dateFormat, timeFormat) : precision !== 'day' ? periodText(savedSingle) : undefined;
985
+ const savedText = saved.mode === 'range' ? formatRangeText(saved.from.value, saved.to.value, type, dateFormat, timeFormat, strings.rangeSeparator, savedDateText, savedStrings) : saved.mode === 'multiple' ? formatMultipleText(saved.slots.map(slot => slot.value), type, dateFormat, timeFormat, savedDateText, savedStrings) : precision !== 'day' ? periodText(savedSingle) : undefined;
675
986
  const periodPlaceholder = precision === 'year' ? strings.placeholderYear : precision === 'month' ? `${strings.placeholderMonth} ${strings.placeholderYear}` : undefined;
676
987
 
677
988
  // The time a custom renderTimeRow edits, and how it sets it (the same
@@ -697,7 +1008,7 @@ const DateTimePicker = props => {
697
1008
  type,
698
1009
  mode,
699
1010
  value,
700
- displayText: savedText ?? formatValueText(savedSingle, type, dateFormat, timeFormat),
1011
+ displayText: digitsFor(savedSystem)(savedText ?? formatValueText(savedSingle, type, dateFormat, timeFormat, savedDateText, savedStrings)),
701
1012
  disabled,
702
1013
  readOnly,
703
1014
  isOpen,
@@ -718,11 +1029,11 @@ const DateTimePicker = props => {
718
1029
  };
719
1030
  const sheetValue = draft.draft.mode === 'single' ? draft.draft.slot.value : null;
720
1031
  // A time outside the limits is explained under the time row, not here.
721
- const sheetValueError = sheetValue && dayOff(sheetValue.date) ? strings.errorNotAllowed : '';
1032
+ const sheetValueError = sheetValue && dayOff(sheetValue.date) ? shownStrings.errorNotAllowed : '';
722
1033
  let rangeError = '';
723
1034
  if (d.mode === 'range' && d.from.value && d.to.value) {
724
1035
  const length = daysBetween(d.from.value.date, d.to.value.date) + 1;
725
- if (constraints.dayStatus(d.from.value.date).disabled || constraints.dayStatus(d.to.value.date).disabled) rangeError = strings.errorNotAllowed;else if (previewIssue === 'RANGE_ORDER') rangeError = strings.errorRangeOrder;else if (previewIssue === 'RANGE_LENGTH') rangeError = range?.minDays !== undefined && length < range.minDays ? strings.errorRangeTooShort(range.minDays) : strings.errorRangeTooLong(range?.maxDays ?? length);
1036
+ if (constraints.dayStatus(d.from.value.date).disabled || constraints.dayStatus(d.to.value.date).disabled) rangeError = shownStrings.errorNotAllowed;else if (previewIssue === 'RANGE_ORDER') rangeError = shownStrings.errorRangeOrder;else if (previewIssue === 'RANGE_LENGTH') rangeError = range?.minDays !== undefined && length < range.minDays ? shownStrings.errorRangeTooShort(range.minDays) : shownStrings.errorRangeTooLong(range?.maxDays ?? length);
726
1037
  }
727
1038
  const fieldCommon = {
728
1039
  type,
@@ -738,8 +1049,11 @@ const DateTimePicker = props => {
738
1049
  };
739
1050
  const trigger = !showField || isInline ? null : renderField ? renderField(state) : /*#__PURE__*/_jsx(ValueField, {
740
1051
  ...fieldCommon,
1052
+ strings: savedStrings,
1053
+ errorStrings: strings,
741
1054
  value: savedSingle,
742
1055
  displayText: savedText,
1056
+ dateText: savedDateText,
743
1057
  allowTyping: mode === 'single' && precision === 'day' && allowFieldTyping,
744
1058
  onTypingDone: onTriggerTyped,
745
1059
  onTyped: () => setTriggerError(''),
@@ -769,8 +1083,11 @@ const DateTimePicker = props => {
769
1083
  testID: testID ? `${testID}-value` : undefined
770
1084
  }) : mode === 'single' ? /*#__PURE__*/_jsx(ValueField, {
771
1085
  ...fieldCommon,
1086
+ strings: draftStrings,
1087
+ errorStrings: shownStrings,
772
1088
  value: sheetValue,
773
1089
  displayText: precision !== 'day' ? periodText(sheetValue) : undefined,
1090
+ dateText: draftDateText,
774
1091
  placeholder: sheetOptions?.fieldPlaceholder ?? periodPlaceholder,
775
1092
  allowTyping: allowTyping && precision === 'day',
776
1093
  onTyped: onSheetTyped,
@@ -783,13 +1100,16 @@ const DateTimePicker = props => {
783
1100
  testID: testID ? `${testID}-value` : undefined
784
1101
  }) : d.mode === 'range' ? /*#__PURE__*/_jsx(RangeFields, {
785
1102
  ...fieldCommon,
1103
+ strings: draftStrings,
1104
+ errorStrings: shownStrings,
786
1105
  layout: rangeLayout,
1106
+ dateText: draftDateText,
787
1107
  showIcon: sheetOptions?.fieldIcon,
788
1108
  from: d.from.value,
789
1109
  to: d.to.value,
790
1110
  placeholder: sheetOptions?.fieldPlaceholder,
791
1111
  activeEnd: activeEnd,
792
- onActivate: setActiveEnd,
1112
+ onActivate: activateEnd,
793
1113
  allowTyping: allowTyping,
794
1114
  onTyped: onRangeTyped,
795
1115
  onFocusChange: (end, f) => reportFocus(`value-${end}`, f),
@@ -800,8 +1120,9 @@ const DateTimePicker = props => {
800
1120
  testID: testID
801
1121
  }) : d.mode === 'multiple' ? /*#__PURE__*/_jsx(ValueField, {
802
1122
  ...fieldCommon,
1123
+ strings: draftStrings,
803
1124
  value: null,
804
- displayText: formatMultipleText(draftValues, type, dateFormat, timeFormat),
1125
+ displayText: formatMultipleText(draftValues, type, dateFormat, timeFormat, draftDateText, draftStrings),
805
1126
  placeholder: sheetOptions?.fieldPlaceholder,
806
1127
  allowClear: allowClear,
807
1128
  onClear: clearInSheet,
@@ -810,37 +1131,102 @@ const DateTimePicker = props => {
810
1131
  active: true,
811
1132
  testID: testID ? `${testID}-value` : undefined
812
1133
  }) : null;
813
- const calendar = folded ? null : /*#__PURE__*/_jsx(Calendar, {
814
- constraints: calendarConstraints,
815
- today: today,
816
- initialVisibleDate: initialVisibleDate,
817
- selection: selection,
818
- onSelectDay: onSelectDay,
819
- firstDayOfWeek: firstDayOfWeek,
820
- showOutsideDays: showOutsideDays,
821
- showHolidays: showHolidays,
822
- yearRange: yearRange,
823
- locale: loc
824
- // Month and year picks are Gregorian whatever the calendar system.
825
- ,
826
- calendarSystem: precision === 'day' ? resolveCalendarSystem(calendarSystem) : 'gregory',
827
- initialView: initialView,
828
- precision: precision,
829
- enableMonthSwipe: enableMonthSwipe,
830
- showToday: showToday,
831
- onVisibleMonthChange: handleVisibleMonth,
832
- renderDay: renderDay,
833
- renderWeekday: renderWeekday,
834
- renderMonthChip: renderMonthChip,
835
- renderYearCell: renderYearCell,
836
- renderMonthCell: renderMonthCell,
837
- renderEmptyState: renderEmptyState,
838
- icons: icons,
839
- iconProps: iconProps,
1134
+ const systemLabels = {
1135
+ gregory: strings.calendarGregorian ?? DEFAULT_STRINGS.calendarGregorian,
1136
+ hijri: strings.calendarHijri ?? DEFAULT_STRINGS.calendarHijri,
1137
+ dual: strings.calendarDual ?? DEFAULT_STRINGS.calendarDual
1138
+ };
1139
+ const systemA11yLabel = strings.a11yCalendarSystem ?? DEFAULT_STRINGS.a11yCalendarSystem;
1140
+ const chooseSystem = next => {
1141
+ if (next === system) return;
1142
+ setChosenSystem(next);
1143
+ if (next === 'dual') setDualTab(dualDefaultTab);
1144
+ echo.current = next;
1145
+ onCalendarSystemChange?.(next);
1146
+ };
1147
+ const calendarSwitchNode = !switchSystems ? null : renderCalendarSwitch ? renderCalendarSwitch({
1148
+ systems: switchSystems,
1149
+ selected: system,
1150
+ onSelect: chooseSystem,
1151
+ tokens,
1152
+ strings
1153
+ }) : /*#__PURE__*/_jsx(CalendarSwitch, {
1154
+ variant: "switch",
1155
+ options: switchSystems.map(v => {
1156
+ return {
1157
+ value: v,
1158
+ label: systemLabels[v]
1159
+ };
1160
+ }),
1161
+ selected: system,
1162
+ onSelect: chooseSystem,
1163
+ accessibilityLabel: systemA11yLabel,
840
1164
  tokens: tokens,
841
1165
  styles: styles,
842
- strings: strings,
843
- testID: testID
1166
+ testID: testID ? `${testID}-calendar-switch` : undefined
1167
+ });
1168
+ const dualTabs = system !== 'dual' ? null : /*#__PURE__*/_jsx(CalendarSwitch, {
1169
+ variant: "tabs",
1170
+ options: [{
1171
+ value: 'gregory',
1172
+ label: systemLabels.gregory
1173
+ }, {
1174
+ value: 'hijri',
1175
+ label: systemLabels.hijri
1176
+ }],
1177
+ selected: dualTab,
1178
+ onSelect: setDualTab,
1179
+ accessibilityLabel: systemA11yLabel,
1180
+ tokens: tokens,
1181
+ styles: styles,
1182
+ testID: testID ? `${testID}-dual-tabs` : undefined
1183
+ });
1184
+ // The Hijri tab lays out its calendar and time rows right to left; the
1185
+ // wrapper always renders, so a tab change doesn't remount the time boxes.
1186
+ const hijriShown = shownSystem === 'hijri';
1187
+ const mirrored = node => node == null ? null : /*#__PURE__*/_jsx(RtlContext.Provider, {
1188
+ value: hijriShown || null,
1189
+ children: /*#__PURE__*/_jsx(View, {
1190
+ style: hijriShown ? RTL : undefined,
1191
+ children: node
1192
+ })
1193
+ });
1194
+ // Another calendar opens on the days the last one showed, else on the
1195
+ // selection (shown or not), else where the picker would open.
1196
+ const calendarAnchor = shownAnchor.current ?? (selectionShown ? null : firstSelectedDay(selection)) ?? initialVisibleDate;
1197
+ const calendar = /*#__PURE__*/_jsxs(View, {
1198
+ style: folded ? FOLDED : undefined,
1199
+ children: [calendarSwitchNode, dualTabs, mirrored(/*#__PURE__*/_jsx(Calendar, {
1200
+ constraints: selectionShown ? calendarConstraints : constraints,
1201
+ today: today,
1202
+ initialVisibleDate: calendarAnchor,
1203
+ selection: selectionShown ? selection : emptySelection(selection),
1204
+ onSelectDay: onSelectDayStable,
1205
+ firstDayOfWeek: firstDayOfWeek,
1206
+ showOutsideDays: showOutsideDays,
1207
+ showHolidays: showHolidays,
1208
+ yearRange: yearRange,
1209
+ locale: shownSystem === 'hijri' ? hijriLocale : loc,
1210
+ calendarSystem: shownSystem,
1211
+ initialView: initialView,
1212
+ precision: precision,
1213
+ enableMonthSwipe: enableMonthSwipe,
1214
+ showToday: showToday,
1215
+ onVisibleMonthChange: handleVisibleMonth,
1216
+ onAnchorChange: rememberAnchor,
1217
+ renderDay: renderDay,
1218
+ renderWeekday: renderWeekday,
1219
+ renderMonthChip: renderMonthChip,
1220
+ renderYearCell: renderYearCell,
1221
+ renderMonthCell: renderMonthCell,
1222
+ renderEmptyState: renderEmptyState,
1223
+ icons: icons,
1224
+ iconProps: iconProps,
1225
+ tokens: tokens,
1226
+ styles: styles,
1227
+ strings: stringsFor(shownSystem),
1228
+ testID: testID
1229
+ }, shownSystem))]
844
1230
  });
845
1231
  const timeRow = type !== 'datetime' ? null : renderTimeRow ? renderTimeRow(state) : /*#__PURE__*/_jsx(TimeRows, {
846
1232
  draft: d,
@@ -867,14 +1253,26 @@ const DateTimePicker = props => {
867
1253
  onPendingChange: onRowPending,
868
1254
  tokens: tokens,
869
1255
  styles: styles,
870
- strings: strings,
1256
+ strings: stringsFor(shownSystem),
871
1257
  testID: testID
872
1258
  });
873
1259
 
874
1260
  // Shown while the selection is still the one it was given for.
875
1261
  const validationMessage = validation && (validation.key === JSON.stringify(draft.toWire()) || validation.key === JSON.stringify(draftToWire(preview, draftOptions))) ? validation.message : null;
1262
+
1263
+ // The field writes the date in the digits of the calendar it was picked in;
1264
+ // the calendar and the time rows follow the tab that shows.
876
1265
  const sheetContent = /*#__PURE__*/_jsxs(_Fragment, {
877
- children: [sheetField, calendar, timeRow, validationMessage ? /*#__PURE__*/_jsx(FieldMessages, {
1266
+ children: [/*#__PURE__*/_jsx(NumeralsContext.Provider, {
1267
+ value: digitsFor(draftSystem),
1268
+ children: sheetField
1269
+ }), /*#__PURE__*/_jsx(NumeralsContext.Provider, {
1270
+ value: digitsFor(shownSystem),
1271
+ children: calendar
1272
+ }), /*#__PURE__*/_jsx(NumeralsContext.Provider, {
1273
+ value: digitsFor(shownSystem),
1274
+ children: mirrored(timeRow)
1275
+ }), validationMessage ? /*#__PURE__*/_jsx(FieldMessages, {
878
1276
  error: validationMessage,
879
1277
  tokens: tokens,
880
1278
  styles: {
@@ -884,7 +1282,8 @@ const DateTimePicker = props => {
884
1282
  testID: testID ? `${testID}-validation` : undefined
885
1283
  }) : null]
886
1284
  });
887
- return /*#__PURE__*/_jsxs(_Fragment, {
1285
+ return /*#__PURE__*/_jsxs(NumeralsContext.Provider, {
1286
+ value: digitsFor(savedSystem),
888
1287
  children: [trigger, /*#__PURE__*/_jsx(PickerContainer, {
889
1288
  presentation: presentation,
890
1289
  isOpen: isOpen,