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
@@ -15,6 +15,7 @@ import type { Time, TimeFormat } from '../../core/types';
15
15
  import type { PickerStrings } from '../theme/defaultStrings';
16
16
  import type { PickerStyles } from '../theme/styleKeys';
17
17
  import { MAX_FONT_SCALE } from '../theme/a11y';
18
+ import { useNumerals } from '../theme/numerals';
18
19
  import type { ThemeTokens } from '../theme/tokens';
19
20
  import { useReduceMotion } from '../hooks/useReduceMotion';
20
21
  import { buildTimeStyles } from './timeStyles';
@@ -31,6 +32,21 @@ export interface WheelItem {
31
32
  disabled: boolean;
32
33
  }
33
34
 
35
+ /**
36
+ * The row showing `value`, or the nearest one when the value has no row (a
37
+ * minute off the step, e.g. 07 on a 5-minute wheel, shows at 05, not 00).
38
+ */
39
+ export const indexOfValue = (items: WheelItem[], value: number): number => {
40
+ const exact = items.findIndex((i) => i.value === value);
41
+ if (exact > -1) return exact;
42
+ let best = 0;
43
+ items.forEach((item, i) => {
44
+ if (Math.abs(item.value - value) < Math.abs(items[best]!.value - value))
45
+ best = i;
46
+ });
47
+ return best;
48
+ };
49
+
34
50
  /** The enabled row nearest to `index` (searching both ways), or `index` when none is. */
35
51
  export const nearestEnabled = (items: WheelItem[], index: number): number => {
36
52
  const start = Math.max(0, Math.min(items.length - 1, index));
@@ -83,13 +99,11 @@ const WheelColumnBase: React.FC<ColumnProps> = ({
83
99
  styles,
84
100
  testID,
85
101
  }) => {
102
+ const digits = useNumerals();
86
103
  const s = buildTimeStyles(tokens);
87
104
  const ref = useRef<ScrollViewRef>(null);
88
105
  const reduceMotion = useReduceMotion();
89
- const selectedIndex = Math.max(
90
- 0,
91
- items.findIndex((i) => i.value === value)
92
- );
106
+ const selectedIndex = indexOfValue(items, value);
93
107
  const [centered, setCentered] = useState(selectedIndex);
94
108
  const spinning = useRef(false);
95
109
  const momentumTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -216,7 +230,7 @@ const WheelColumnBase: React.FC<ColumnProps> = ({
216
230
  ],
217
231
  ]}
218
232
  >
219
- {item.label}
233
+ {digits(item.label)}
220
234
  </Text>
221
235
  </View>
222
236
  ))}
@@ -23,13 +23,13 @@ const create = (t: ThemeTokens) =>
23
23
  fontSize: t.fontSizeLarge,
24
24
  color: t.heading,
25
25
  },
26
- // Times read hh:mm in every language, so the boxes don't mirror in RTL.
27
26
  boxes: {
28
27
  flexDirection: 'row',
29
28
  alignItems: 'center',
30
29
  gap: 22,
31
- direction: 'ltr',
32
30
  },
31
+ // The boxes follow the row's direction: right to left (the Hijri tab, or a
32
+ // right-to-left app), the hour sits on the right and AM / PM on the left.
33
33
  clock: { flexDirection: 'row', alignItems: 'center' },
34
34
  // Each box is 50 × 46 with a 1 pt border and radius 6.
35
35
  boxWrap: { width: 50 },
@@ -104,7 +104,6 @@ const create = (t: ThemeTokens) =>
104
104
  flexDirection: 'row',
105
105
  alignItems: 'center',
106
106
  alignSelf: 'center',
107
- direction: 'ltr',
108
107
  },
109
108
  wheelColumn: { width: 64, height: 200, overflow: 'hidden' },
110
109
  wheelHighlight: {
@@ -1,6 +1,8 @@
1
1
  import type React from 'react';
2
2
  import type { StyleProp, TextStyle } from 'react-native';
3
+ import type { CalendarSystem } from '../core/calendarSystem';
3
4
  import type { Constraints, TimeBounds } from '../core/constraints';
5
+ import type { Numerals } from '../core/numerals';
4
6
  import type {
5
7
  CalendarDate,
6
8
  PickerType,
@@ -35,7 +37,7 @@ export interface PickerState {
35
37
  disabled: boolean;
36
38
  readOnly: boolean;
37
39
  isOpen: boolean;
38
- /** The keyboard is up inside the picker; the calendar folds away. */
40
+ /** The keyboard is up inside the picker and `hideCalendarOnKeyboard` hid the calendar. */
39
41
  folded: boolean;
40
42
  /**
41
43
  * Confirm can be pressed: the dates are complete and allowed. A time with a
@@ -102,6 +104,18 @@ export interface SheetOptions {
102
104
  height?: SheetSize;
103
105
  /** Default '90%'. Built-in sheet only. */
104
106
  maxHeight?: SheetSize;
107
+ /**
108
+ * The most the sheet grows to while the keyboard is up; a percentage is of
109
+ * the room above the keyboard. Default: `maxHeight`. Built-in sheet only.
110
+ */
111
+ keyboardMaxHeight?: SheetSize;
112
+ /**
113
+ * Space kept free below Confirm while the keyboard is down: the home
114
+ * indicator, or Android's navigation bar in an edge-to-edge app. Default:
115
+ * the bottom inset from react-native-safe-area-context's SafeAreaProvider
116
+ * when the app has one, else 0. Built-in sheet only.
117
+ */
118
+ bottomInset?: number;
105
119
  /** The header stays put while the body scrolls. false: it scrolls with the body. Default true. */
106
120
  fixedHeader?: boolean;
107
121
  /** The footer (Confirm) stays pinned below the body. false: it scrolls at the end of the body. Default true. */
@@ -118,6 +132,27 @@ export interface SheetOptions {
118
132
  fieldIcon?: boolean;
119
133
  }
120
134
 
135
+ /**
136
+ * The second argument of `onChange` when the picker can show a Hijri calendar:
137
+ * the calendar the date was picked in. The value itself is always Gregorian.
138
+ * The picker remembers it while mounted; to show a stored value back the same
139
+ * way later, store this too and pass it as `dualDefaultTab` (dual) or
140
+ * `calendarSystem`.
141
+ */
142
+ export interface ChangeInfo {
143
+ calendarSystem: 'gregory' | 'hijri';
144
+ }
145
+
146
+ /** What `renderCalendarSwitch` receives. */
147
+ export interface CalendarSwitchRenderProps {
148
+ /** The choices, in order. */
149
+ systems: CalendarSystem[];
150
+ selected: CalendarSystem;
151
+ onSelect: (system: CalendarSystem) => void;
152
+ tokens: ThemeTokens;
153
+ strings: PickerStrings;
154
+ }
155
+
121
156
  /** What `renderLabel` receives. */
122
157
  export interface LabelRenderProps {
123
158
  label?: string;
@@ -167,8 +202,10 @@ export interface DateTimePickerProps {
167
202
  /**
168
203
  * A value was saved: on Confirm; as it's edited with commitMode 'instant';
169
204
  * when typing in the on-screen field pauses; or '' on clear. Never on close.
205
+ * When the picker can show a Hijri calendar, `info` says which calendar the
206
+ * date was picked in.
170
207
  */
171
- onChange: (value: PickerValue) => void;
208
+ onChange: (value: PickerValue, info?: ChangeInfo) => void;
172
209
  /**
173
210
  * Right after every onChange that saves a value (Confirm, an 'instant' edit,
174
211
  * on-screen typing), like a form field losing focus. Not on clear.
@@ -213,6 +250,12 @@ export interface DateTimePickerProps {
213
250
  readOnly?: boolean;
214
251
  /** Type date and time as segments in the field inside the sheet. */
215
252
  allowTyping?: boolean;
253
+ /**
254
+ * Hide the calendar while the keyboard is up (typing a date or a time), so
255
+ * the box being typed in and Confirm stay in view on small phones. Default
256
+ * true; `false` keeps the calendar in view.
257
+ */
258
+ hideCalendarOnKeyboard?: boolean;
216
259
  /**
217
260
  * The on-screen field is typeable too. A complete, allowed value is saved
218
261
  * once, when focus leaves the field or its last box fills. Default false.
@@ -277,10 +320,42 @@ export interface DateTimePickerProps {
277
320
  initialVisibleDate?: CalendarDate;
278
321
  enableMonthSwipe?: boolean;
279
322
  showToday?: boolean;
280
- /** Default 'gregory'. Falls back to 'gregory' when the platform has no Hijri calendar. */
281
- calendarSystem?: 'gregory' | 'dual' | 'hijri';
323
+ /**
324
+ * The calendar the grid uses: 'gregory', 'hijri' (Umm al-Qura), or 'dual':
325
+ * Gregorian and Hijri tabs, one calendar at a time. A day is shown selected
326
+ * only in the calendar it was picked in, and the field writes it in that
327
+ * calendar. With `calendarSwitch`, this is the one shown first. Default
328
+ * 'gregory'; anything Hijri falls back to 'gregory' when the platform has no
329
+ * Hijri calendar.
330
+ */
331
+ calendarSystem?: CalendarSystem;
332
+ /**
333
+ * A Gregorian / Hijri / Dual switch above the calendar. true shows all three;
334
+ * a list shows those, in that order (e.g. ['hijri', 'gregory']). Not shown for
335
+ * month and year picks, or when the platform has no Hijri calendar. Default false.
336
+ */
337
+ calendarSwitch?: boolean | CalendarSystem[];
338
+ /** The user picked another calendar in the switch. */
339
+ onCalendarSystemChange?: (system: CalendarSystem) => void;
340
+ /**
341
+ * The language a Hijri calendar is written in, whatever `locale` is: its
342
+ * month and weekday names, the Hijri date in the field, and what screen
343
+ * readers say. Default 'ar-SA' (Arabic).
344
+ */
345
+ hijriLocale?: string;
346
+ /** The tab a dual calendar opens on, each time it opens. Default 'gregory'. */
347
+ dualDefaultTab?: 'gregory' | 'hijri';
348
+ /** Replaces the built-in calendar switch. */
349
+ renderCalendarSwitch?: (p: CalendarSwitchRenderProps) => React.ReactNode;
282
350
  /** Any BCP-47 tag; decides names, the date order and the clock. Default: the device locale. */
283
351
  locale?: string;
352
+ /**
353
+ * The digits every number is drawn in: 'latin' (0–9), 'arabic' (١٢٣ in any
354
+ * language) or 'locale' (the language's own: ١٢٣ for 'ar-SA', ۱۲۳ for
355
+ * 'fa-IR', 123 for 'en-US'). Values stay 0–9. Default: 'arabic' for a Hijri
356
+ * calendar and a date picked in it, 'latin' for a Gregorian one.
357
+ */
358
+ numerals?: Numerals;
284
359
  /** IANA zone for date-time values and "today". Default: the device zone. */
285
360
  timeZone?: string;
286
361
  /** The current time, for "today" and a new day's time. Default `() => new Date()`. */
@@ -316,6 +391,13 @@ export interface DateTimePickerProps {
316
391
  theme?: Partial<ThemeTokens>;
317
392
  styles?: PickerStyles;
318
393
  strings?: Partial<PickerStrings>;
394
+ /**
395
+ * The texts while the Hijri tab shows (e.g. Arabic), for its calendar and
396
+ * time rows, the error messages inside the picker, and a date picked in it
397
+ * (the on-screen field's errors keep `strings`). A key left out (or `undefined`)
398
+ * keeps AM / PM in `hijriLocale` (ص / م), then `strings`.
399
+ */
400
+ hijriStrings?: Partial<PickerStrings>;
319
401
  testID?: string;
320
402
 
321
403
  // events
@@ -1,13 +1,15 @@
1
1
  import { addDays, addMonths, daysInMonth } from './calendarDate';
2
2
  import type { CalendarDate, Weekday } from './types';
3
3
  import { weekdayOf } from './calendarDate';
4
+ import { toLatinDigits } from './digits';
5
+ import { withCalendar } from './locale';
4
6
 
5
7
  /**
6
- * Calendar systems for the picker grid. Saved values are always Gregorian;
7
- * a system only changes how months are laid out and labelled.
8
+ * Calendar systems for the picker. Saved values are always Gregorian; a
9
+ * system only changes how months are laid out and labelled.
8
10
  * - 'gregory': the normal grid;
9
- * - 'dual': the Gregorian grid with the Hijri day under each day;
10
- * - 'hijri': a Hijri (Umm al-Qura) grid showing only the Hijri day.
11
+ * - 'hijri': a Hijri (Umm al-Qura) grid showing the Hijri day;
12
+ * - 'dual': both, as Gregorian and Hijri tabs showing one grid at a time.
11
13
  *
12
14
  * Hijri dates come from the platform's Intl ('islamic-umalqura'). When Intl
13
15
  * can't do it (checked once; some JS engines ship without that calendar) the
@@ -57,16 +59,9 @@ const utcDate = (
57
59
  return d;
58
60
  };
59
61
 
60
- /**
61
- * The locale with the Umm al-Qura calendar, keeping any Unicode extension it
62
- * already has ('ar-SA-u-nu-arab' → 'ar-SA-u-ca-islamic-umalqura-nu-arab').
63
- */
64
- const withHijriCalendar = (locale: string): string => {
65
- const [base, ext] = locale.split(/-u-/i);
66
- if (ext === undefined) return `${locale}-u-ca-islamic-umalqura`;
67
- const rest = ext.replace(/(^|-)ca(-[a-z0-9]{3,8})+/i, '').replace(/^-/, '');
68
- return `${base}-u-ca-islamic-umalqura${rest ? `-${rest}` : ''}`;
69
- };
62
+ /** The locale with the Umm al-Qura calendar ('ar-SA-u-nu-arab' → 'ar-SA-u-ca-islamic-umalqura-nu-arab'). */
63
+ const withHijriCalendar = (locale: string): string =>
64
+ withCalendar(locale, 'islamic-umalqura');
70
65
 
71
66
  const readParts = (
72
67
  f: Intl.DateTimeFormat,
@@ -83,19 +78,45 @@ const readParts = (
83
78
  : null;
84
79
  };
85
80
 
81
+ /**
82
+ * Remembers results by key. Every tap redraws the grid and its labels, and
83
+ * Intl is slow on some engines (Hermes on Android), so Hijri conversions are
84
+ * worked out once. Cleared when full, to stay small.
85
+ */
86
+ const memoize = <V>(limit = 4000) => {
87
+ const map = new Map<string, V>();
88
+ return {
89
+ get: (key: string, make: () => V): V => {
90
+ if (map.has(key)) return map.get(key)!;
91
+ const value = make();
92
+ if (map.size >= limit) map.clear();
93
+ map.set(key, value);
94
+ return value;
95
+ },
96
+ clear: () => map.clear(),
97
+ };
98
+ };
99
+ const hijriDays = memoize<CalendarDate | null>();
100
+ const hijriStarts = memoize<CalendarDate>(600);
101
+ const hijriTexts = memoize<string | null>();
102
+
86
103
  /** Whether this device can show Hijri dates. */
87
104
  export const isHijriSupported = (): boolean => getHijriFormat() !== null;
88
105
 
89
- /** Test hook: forget the cached Intl check. */
106
+ /** Test hook: forget the cached Intl check and what was worked out with it. */
90
107
  export const resetHijriSupportCache = (): void => {
91
108
  hijriFormat = undefined;
109
+ hijriDays.clear();
110
+ hijriStarts.clear();
111
+ hijriTexts.clear();
92
112
  };
93
113
 
94
114
  /** Gregorian day → Hijri {year, month, day}, or null when unsupported. */
95
- export const toHijri = (d: CalendarDate): CalendarDate | null => {
96
- const f = getHijriFormat();
97
- return f ? readParts(f, d) : null;
98
- };
115
+ export const toHijri = (d: CalendarDate): CalendarDate | null =>
116
+ hijriDays.get(`${d.year}-${d.month}-${d.day}`, () => {
117
+ const f = getHijriFormat();
118
+ return f ? readParts(f, d) : null;
119
+ });
99
120
 
100
121
  const HIJRI_EPOCH = { year: 622, month: 7, day: 19 };
101
122
  const MEAN_YEAR = 354.367;
@@ -105,7 +126,10 @@ const cmp = (a: SystemMonth, b: SystemMonth) =>
105
126
  a.year - b.year || a.month - b.month;
106
127
 
107
128
  /** The Gregorian day on which a Hijri month starts. */
108
- export const hijriMonthStart = (year: number, month: number): CalendarDate => {
129
+ export const hijriMonthStart = (year: number, month: number): CalendarDate =>
130
+ hijriStarts.get(`${year}-${month}`, () => findMonthStart(year, month));
131
+
132
+ const findMonthStart = (year: number, month: number): CalendarDate => {
109
133
  const estimate = Math.round(
110
134
  (year - 1) * MEAN_YEAR + (month - 1) * MEAN_MONTH
111
135
  );
@@ -125,7 +149,11 @@ export const hijriMonthStart = (year: number, month: number): CalendarDate => {
125
149
 
126
150
  export const addHijriMonths = (m: SystemMonth, n: number): SystemMonth => {
127
151
  const index = m.year * 12 + (m.month - 1) + n;
128
- return { year: Math.floor(index / 12), month: (index % 12) + 1 };
152
+ // % keeps the sign in JS, so a year before 1 AH still gets months 1–12.
153
+ return {
154
+ year: Math.floor(index / 12),
155
+ month: (((index % 12) + 12) % 12) + 1,
156
+ };
129
157
  };
130
158
 
131
159
  export const hijriMonthLength = (year: number, month: number): number => {
@@ -154,13 +182,26 @@ export const HIJRI_MONTHS_EN = [
154
182
  'Dhuʻl-Hijjah',
155
183
  ];
156
184
 
185
+ const monthNameCache = new Map<string, string[]>();
186
+
157
187
  /** Hijri month names in the user's language (Intl), English otherwise. */
158
188
  export const hijriMonthNames = (locale: string): string[] => {
189
+ const hit = monthNameCache.get(locale);
190
+ if (hit) return hit;
191
+ const names = readHijriMonthNames(locale);
192
+ monthNameCache.set(locale, names);
193
+ return names;
194
+ };
195
+
196
+ const readHijriMonthNames = (locale: string): string[] => {
159
197
  try {
160
198
  const f = new Intl.DateTimeFormat(withHijriCalendar(locale), {
161
199
  timeZone: 'UTC',
162
200
  month: 'long',
163
201
  });
202
+ // A locale Intl doesn't know drops the calendar and would give Gregorian names.
203
+ if (f.resolvedOptions().calendar !== 'islamic-umalqura')
204
+ return HIJRI_MONTHS_EN;
164
205
  return HIJRI_MONTHS_EN.map((fallback, i) => {
165
206
  const start = hijriMonthStart(1448, i + 1);
166
207
  const name = f.format(
@@ -173,6 +214,89 @@ export const hijriMonthNames = (locale: string): string[] => {
173
214
  }
174
215
  };
175
216
 
217
+ const dateTextFormats = new Map<string, Intl.DateTimeFormat | null>();
218
+
219
+ /**
220
+ * A Gregorian day written as a Hijri date in the user's language, in 0–9
221
+ * ('16 Jumada I 1448 AH', '16 جمادى الأولى 1448 هـ'), or null when unsupported.
222
+ */
223
+ export const hijriDateText = (d: CalendarDate, locale: string): string | null =>
224
+ hijriTexts.get(`${locale}|${d.year}-${d.month}-${d.day}`, () =>
225
+ writeHijriDate(d, locale)
226
+ );
227
+
228
+ const writeHijriDate = (d: CalendarDate, locale: string): string | null => {
229
+ const h = toHijri(d);
230
+ if (!h) return null;
231
+ if (!dateTextFormats.has(locale)) {
232
+ let f: Intl.DateTimeFormat | null = null;
233
+ try {
234
+ f = new Intl.DateTimeFormat(withHijriCalendar(locale), {
235
+ timeZone: 'UTC',
236
+ day: 'numeric',
237
+ month: 'long',
238
+ year: 'numeric',
239
+ });
240
+ } catch {
241
+ f = null;
242
+ }
243
+ // A locale Intl doesn't know drops the calendar: build the text instead.
244
+ dateTextFormats.set(
245
+ locale,
246
+ f?.resolvedOptions().calendar === 'islamic-umalqura' ? f : null
247
+ );
248
+ }
249
+ const f = dateTextFormats.get(locale);
250
+ const text = f
251
+ ? toLatinDigits(f.format(utcDate(d.year, d.month - 1, d.day, 12)))
252
+ : '';
253
+ // An engine that ignores the calendar writes the Gregorian year: build it instead.
254
+ if (text.includes(String(h.year))) return text;
255
+ return `${h.day} ${hijriMonthNames(locale)[h.month - 1]} ${h.year}`;
256
+ };
257
+
258
+ export interface Periods {
259
+ am: string;
260
+ pm: string;
261
+ }
262
+
263
+ const ARABIC_PERIODS: Periods = { am: 'ص', pm: 'م' };
264
+ const periodCache = new Map<string, Periods | null>();
265
+
266
+ /**
267
+ * AM and PM in the Hijri locale's language, as Intl writes them ('ar-SA' →
268
+ * ص / م). An Arabic locale whose engine gives none, or Latin ones, gets ص / م;
269
+ * another locale gets null (the picker keeps strings.am / pm).
270
+ */
271
+ export const hijriPeriods = (locale: string): Periods | null => {
272
+ if (!periodCache.has(locale)) periodCache.set(locale, readPeriods(locale));
273
+ return periodCache.get(locale)!;
274
+ };
275
+
276
+ const readPeriods = (locale: string): Periods | null => {
277
+ const arabic = /^ar\b/i.test(locale);
278
+ try {
279
+ const f = new Intl.DateTimeFormat(locale, {
280
+ timeZone: 'UTC',
281
+ hour: 'numeric',
282
+ hour12: true,
283
+ });
284
+ const period = (hour: number) =>
285
+ f
286
+ .formatToParts(utcDate(2026, 0, 1, hour))
287
+ // Some engines write the part type in lower case.
288
+ .find((p) => p.type.toLowerCase() === 'dayperiod')
289
+ ?.value.trim();
290
+ const am = period(9);
291
+ const pm = period(21);
292
+ if (am && pm && am !== pm && !(arabic && /[A-Za-z]/.test(am + pm)))
293
+ return { am, pm };
294
+ } catch {
295
+ // An engine without this locale falls through.
296
+ }
297
+ return arabic ? ARABIC_PERIODS : null;
298
+ };
299
+
176
300
  /**
177
301
  * The system the picker uses: the requested one, Gregorian by default.
178
302
  * Anything Hijri falls back to Gregorian when unsupported.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The zero of each decimal digit block a keyboard may type in: Arabic-Indic
3
+ * (٠–٩), Persian (۰–۹), N'Ko, the Indic scripts, Thai, Lao, Tibetan, Myanmar,
4
+ * Khmer, Mongolian and full-width. Each block runs zero to nine in order.
5
+ */
6
+ const DIGIT_ZEROS = [
7
+ 0x0660, 0x06f0, 0x07c0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0be6,
8
+ 0x0c66, 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x17e0, 0x1810,
9
+ 0xff10,
10
+ ];
11
+
12
+ /** Any decimal digits as 0–9 ('٢٥' → '25'); other characters are kept. */
13
+ export const toLatinDigits = (text: string): string => {
14
+ let out = '';
15
+ for (const ch of text) {
16
+ const code = ch.charCodeAt(0);
17
+ const zero = DIGIT_ZEROS.find((z) => code >= z && code <= z + 9);
18
+ out += zero === undefined ? ch : String(code - zero);
19
+ }
20
+ return out;
21
+ };
package/src/core/draft.ts CHANGED
@@ -37,7 +37,15 @@ export interface DraftSlot {
37
37
  export type Draft =
38
38
  | { mode: 'single'; slot: DraftSlot }
39
39
  | { mode: 'range'; from: DraftSlot; to: DraftSlot }
40
- | { mode: 'multiple'; slots: DraftSlot[]; sharedTime: Time | null };
40
+ | {
41
+ mode: 'multiple';
42
+ slots: DraftSlot[];
43
+ /**
44
+ * Shared time mode: the time every date gets. Null while the saved dates
45
+ * have different times, which each date then keeps until one is set.
46
+ */
47
+ sharedTime: Time | null;
48
+ };
41
49
 
42
50
  export interface DraftOptions {
43
51
  type: PickerType;
@@ -119,13 +127,22 @@ export const createDraft = (
119
127
  return { mode, from: slotFrom(from, opts), to: slotFrom(to, opts) };
120
128
  }
121
129
  if (mode === 'multiple') {
130
+ // One slot per day: a later value on a day already listed is dropped.
122
131
  const slots = sortSlots(
123
132
  splitMultiple(safe)
124
133
  .map((w) => slotFrom(w, opts))
125
- .filter((s) => s.value)
134
+ .filter(
135
+ (s, i, all) =>
136
+ s.value &&
137
+ all.findIndex((o) => isSameDay(o.value?.date, s.value?.date)) === i
138
+ )
139
+ );
140
+ const times = slots.map((s) => s.value?.time ?? null);
141
+ const first = times[0] ?? null;
142
+ const sameTime = times.every(
143
+ (t) => t?.hour === first?.hour && t?.minute === first?.minute
126
144
  );
127
- const sharedTime =
128
- opts.type === 'datetime' ? (slots[0]?.value?.time ?? null) : null;
145
+ const sharedTime = opts.type === 'datetime' && sameTime ? first : null;
129
146
  return { mode, slots, sharedTime };
130
147
  }
131
148
  return {
@@ -158,6 +175,14 @@ const withDay = (
158
175
  };
159
176
  };
160
177
 
178
+ /**
179
+ * Whether a range may start and end on one day: date-time ranges and
180
+ * `allowSameDay` date ranges, as long as minDays allows a 1-day range.
181
+ */
182
+ export const sameDayRangeOk = (opts: DraftOptions): boolean =>
183
+ (opts.type === 'datetime' || !!opts.range?.allowSameDay) &&
184
+ (opts.range?.minDays ?? 1) <= 1;
185
+
161
186
  const pickRangeDay = (
162
187
  draft: Extract<Draft, { mode: 'range' }>,
163
188
  day: CalendarDate,
@@ -166,10 +191,6 @@ const pickRangeDay = (
166
191
  ): Draft => {
167
192
  const from = draft.from.value?.date;
168
193
  const to = draft.to.value?.date;
169
- // A same-day range is 1 day long, so it's only an end when minDays allows it.
170
- const allowSameDay =
171
- (opts.type === 'datetime' || !!opts.range?.allowSameDay) &&
172
- (opts.range?.minDays ?? 1) <= 1;
173
194
 
174
195
  if (!from) return { ...draft, from: withDay(draft.from, day, opts, eq) };
175
196
 
@@ -177,7 +198,8 @@ const pickRangeDay = (
177
198
  const order = compareCalendarDates(day, from);
178
199
  if (order < 0)
179
200
  return { ...draft, from: withDay(draft.from, day, opts, eq) };
180
- if (order === 0 && !allowSameDay)
201
+ // The start tapped again, where it can't also be the end: deselects it.
202
+ if (order === 0 && !sameDayRangeOk(opts))
181
203
  return { ...draft, from: { ...draft.from, value: null } };
182
204
  return { ...draft, to: withDay(draft.to, day, opts, eq) };
183
205
  }
@@ -228,8 +250,10 @@ export const selectDay = (
228
250
  ...draft.slots,
229
251
  { value: { date: day, time }, initialWire: '' },
230
252
  ]),
253
+ // The first date's time becomes the shared one; dates with their own
254
+ // (different) times keep them.
231
255
  sharedTime:
232
- opts.type === 'datetime' && !perDate
256
+ opts.type === 'datetime' && !perDate && draft.slots.length === 0
233
257
  ? (draft.sharedTime ?? time)
234
258
  : draft.sharedTime,
235
259
  };
@@ -237,7 +261,9 @@ export const selectDay = (
237
261
 
238
262
  /**
239
263
  * From/To layout: a tap fills the end being edited. An end that would come
240
- * before the start (or after the end) clears the other one.
264
+ * before the start (or after the end) clears the other one. The start tapped
265
+ * again as the end, where a range can't be one day, deselects the start, as
266
+ * in the single field.
241
267
  */
242
268
  export const setRangeEnd = (
243
269
  draft: Draft,
@@ -247,12 +273,14 @@ export const setRangeEnd = (
247
273
  newDayTime?: Time | null
248
274
  ): Draft => {
249
275
  if (draft.mode !== 'range') return draft;
250
- const sameDayOk = opts.type === 'datetime' || !!opts.range?.allowSameDay;
276
+ const sameDayOk = sameDayRangeOk(opts);
251
277
  const fits = (from?: CalendarDate, to?: CalendarDate) => {
252
278
  if (!from || !to) return true;
253
279
  const order = compareCalendarDates(to, from);
254
280
  return order > 0 || (order === 0 && sameDayOk);
255
281
  };
282
+ if (end === 'to' && !sameDayOk && isSameDay(draft.from.value?.date, day))
283
+ return { ...draft, from: { ...draft.from, value: null } };
256
284
  if (end === 'from') {
257
285
  const from = withDay(draft.from, day, opts, newDayTime);
258
286
  const keepTo = fits(day, draft.to.value?.date);
@@ -346,7 +374,7 @@ const effectiveValue = (
346
374
  opts.type === 'datetime' &&
347
375
  opts.multiple?.timeMode !== 'perDate'
348
376
  ) {
349
- return { ...slot.value, time: draft.sharedTime };
377
+ return { ...slot.value, time: draft.sharedTime ?? slot.value.time };
350
378
  }
351
379
  return slot.value;
352
380
  };
@@ -8,10 +8,24 @@ import type { TimeFormat, Weekday } from './types';
8
8
  const FALLBACK_LOCALE = 'en-US';
9
9
  const FALLBACK_DATE_FORMAT = 'yyyy-MM-dd';
10
10
  const SEPARATORS = ['/', '-', '.'];
11
- const DATE_TOKENS: Partial<Record<Intl.DateTimeFormatPartTypes, string>> = {
11
+ const DATE_TOKENS: Partial<Record<string, string>> = {
12
12
  day: 'dd',
13
13
  month: 'MM',
14
14
  year: 'yyyy',
15
+ // Hermes on Android labels the year of some locales (ar-SA) this way.
16
+ yearName: 'yyyy',
17
+ };
18
+
19
+ /**
20
+ * The locale with a calendar, keeping any Unicode extension it already has
21
+ * ('ar-SA-u-nu-arab' → 'ar-SA-u-ca-gregory-nu-arab'). Some engines write a
22
+ * locale in its own calendar by default (iOS: Hijri for 'ar-SA').
23
+ */
24
+ export const withCalendar = (locale: string, calendar: string): string => {
25
+ const [base, ext] = locale.split(/-u-/i);
26
+ if (ext === undefined) return `${locale}-u-ca-${calendar}`;
27
+ const rest = ext.replace(/(^|-)ca(-[a-z0-9]{3,8})+/i, '').replace(/^-/, '');
28
+ return `${base}-u-ca-${calendar}${rest ? `-${rest}` : ''}`;
15
29
  };
16
30
 
17
31
  /** The device locale (a BCP-47 tag), or 'en-US'. */
@@ -31,7 +45,7 @@ export const defaultLocale = (): string => {
31
45
  */
32
46
  export const deriveDateFormat = (locale: string): string => {
33
47
  try {
34
- const parts = new Intl.DateTimeFormat(locale, {
48
+ const parts = new Intl.DateTimeFormat(withCalendar(locale, 'gregory'), {
35
49
  year: 'numeric',
36
50
  month: '2-digit',
37
51
  day: '2-digit',
@@ -42,7 +56,10 @@ export const deriveDateFormat = (locale: string): string => {
42
56
  !['dd', 'MM', 'yyyy'].every((t) => tokens.includes(t))
43
57
  )
44
58
  return FALLBACK_DATE_FORMAT;
45
- const first = parts.find((p) => p.type === 'literal')?.value ?? '';
59
+ // RTL locales mark the separator with a direction mark ('\u200F/').
60
+ const first = (
61
+ parts.find((p) => p.type === 'literal')?.value ?? ''
62
+ ).replace(/[\u200E\u200F\u061C]/g, '');
46
63
  const separator = SEPARATORS.includes(first) ? first : '/';
47
64
  return tokens.join(separator);
48
65
  } catch {