@terreno/ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +160 -0
  3. package/dist/Accordion.d.ts +3 -0
  4. package/dist/Accordion.js +30 -0
  5. package/dist/Accordion.js.map +1 -0
  6. package/dist/ActionSheet.d.ts +169 -0
  7. package/dist/ActionSheet.js +637 -0
  8. package/dist/ActionSheet.js.map +1 -0
  9. package/dist/AddressField.d.ts +3 -0
  10. package/dist/AddressField.js +18 -0
  11. package/dist/AddressField.js.map +1 -0
  12. package/dist/Avatar.d.ts +3 -0
  13. package/dist/Avatar.js +189 -0
  14. package/dist/Avatar.js.map +1 -0
  15. package/dist/Badge.d.ts +3 -0
  16. package/dist/Badge.js +100 -0
  17. package/dist/Badge.js.map +1 -0
  18. package/dist/Banner.d.ts +4 -0
  19. package/dist/Banner.js +103 -0
  20. package/dist/Banner.js.map +1 -0
  21. package/dist/Body.d.ts +3 -0
  22. package/dist/Body.js +17 -0
  23. package/dist/Body.js.map +1 -0
  24. package/dist/BooleanField.d.ts +3 -0
  25. package/dist/BooleanField.js +89 -0
  26. package/dist/BooleanField.js.map +1 -0
  27. package/dist/Box.d.ts +3 -0
  28. package/dist/Box.js +289 -0
  29. package/dist/Box.js.map +1 -0
  30. package/dist/Button.d.ts +3 -0
  31. package/dist/Button.js +105 -0
  32. package/dist/Button.js.map +1 -0
  33. package/dist/Card.d.ts +2 -0
  34. package/dist/Card.js +18 -0
  35. package/dist/Card.js.map +1 -0
  36. package/dist/CheckBox.d.ts +3 -0
  37. package/dist/CheckBox.js +28 -0
  38. package/dist/CheckBox.js.map +1 -0
  39. package/dist/Common.d.ts +2325 -0
  40. package/dist/Common.js +47 -0
  41. package/dist/Common.js.map +1 -0
  42. package/dist/CommonIconTypes.d.ts +3 -0
  43. package/dist/CommonIconTypes.js +2 -0
  44. package/dist/CommonIconTypes.js.map +1 -0
  45. package/dist/Constants.d.ts +12685 -0
  46. package/dist/Constants.js +3310 -0
  47. package/dist/Constants.js.map +1 -0
  48. package/dist/CustomSelectField.d.ts +3 -0
  49. package/dist/CustomSelectField.js +59 -0
  50. package/dist/CustomSelectField.js.map +1 -0
  51. package/dist/DataTable.d.ts +3 -0
  52. package/dist/DataTable.js +190 -0
  53. package/dist/DataTable.js.map +1 -0
  54. package/dist/DateTimeActionSheet.d.ts +2 -0
  55. package/dist/DateTimeActionSheet.js +270 -0
  56. package/dist/DateTimeActionSheet.js.map +1 -0
  57. package/dist/DateTimeField.d.ts +3 -0
  58. package/dist/DateTimeField.js +514 -0
  59. package/dist/DateTimeField.js.map +1 -0
  60. package/dist/DateUtilities.d.ts +57 -0
  61. package/dist/DateUtilities.js +308 -0
  62. package/dist/DateUtilities.js.map +1 -0
  63. package/dist/DecimalRangeActionSheet.d.ts +6 -0
  64. package/dist/DecimalRangeActionSheet.js +45 -0
  65. package/dist/DecimalRangeActionSheet.js.map +1 -0
  66. package/dist/DismissButton.d.ts +3 -0
  67. package/dist/DismissButton.js +12 -0
  68. package/dist/DismissButton.js.map +1 -0
  69. package/dist/EmailField.d.ts +3 -0
  70. package/dist/EmailField.js +48 -0
  71. package/dist/EmailField.js.map +1 -0
  72. package/dist/EmojiSelector.d.ts +113 -0
  73. package/dist/EmojiSelector.js +322 -0
  74. package/dist/EmojiSelector.js.map +1 -0
  75. package/dist/ErrorBoundary.d.ts +19 -0
  76. package/dist/ErrorBoundary.js +30 -0
  77. package/dist/ErrorBoundary.js.map +1 -0
  78. package/dist/ErrorPage.d.ts +6 -0
  79. package/dist/ErrorPage.js +15 -0
  80. package/dist/ErrorPage.js.map +1 -0
  81. package/dist/Field.d.ts +3 -0
  82. package/dist/Field.js +80 -0
  83. package/dist/Field.js.map +1 -0
  84. package/dist/FlatList.d.ts +2 -0
  85. package/dist/FlatList.js +3 -0
  86. package/dist/FlatList.js.map +1 -0
  87. package/dist/Heading.d.ts +3 -0
  88. package/dist/Heading.js +43 -0
  89. package/dist/Heading.js.map +1 -0
  90. package/dist/HeightActionSheet.d.ts +11 -0
  91. package/dist/HeightActionSheet.js +46 -0
  92. package/dist/HeightActionSheet.js.map +1 -0
  93. package/dist/Hyperlink.d.ts +30 -0
  94. package/dist/Hyperlink.js +144 -0
  95. package/dist/Hyperlink.js.map +1 -0
  96. package/dist/Icon.d.ts +3 -0
  97. package/dist/Icon.js +15 -0
  98. package/dist/Icon.js.map +1 -0
  99. package/dist/IconButton.d.ts +3 -0
  100. package/dist/IconButton.js +111 -0
  101. package/dist/IconButton.js.map +1 -0
  102. package/dist/Image.d.ts +8 -0
  103. package/dist/Image.js +37 -0
  104. package/dist/Image.js.map +1 -0
  105. package/dist/ImageBackground.d.ts +10 -0
  106. package/dist/ImageBackground.js +9 -0
  107. package/dist/ImageBackground.js.map +1 -0
  108. package/dist/InfoModalIcon.d.ts +3 -0
  109. package/dist/InfoModalIcon.js +10 -0
  110. package/dist/InfoModalIcon.js.map +1 -0
  111. package/dist/InfoTooltipButton.d.ts +3 -0
  112. package/dist/InfoTooltipButton.js +6 -0
  113. package/dist/InfoTooltipButton.js.map +1 -0
  114. package/dist/Link.d.ts +3 -0
  115. package/dist/Link.js +10 -0
  116. package/dist/Link.js.map +1 -0
  117. package/dist/MarkdownView.d.ts +5 -0
  118. package/dist/MarkdownView.js +44 -0
  119. package/dist/MarkdownView.js.map +1 -0
  120. package/dist/MediaQuery.d.ts +4 -0
  121. package/dist/MediaQuery.js +52 -0
  122. package/dist/MediaQuery.js.map +1 -0
  123. package/dist/MobileAddressAutoComplete.d.ts +2 -0
  124. package/dist/MobileAddressAutoComplete.js +54 -0
  125. package/dist/MobileAddressAutoComplete.js.map +1 -0
  126. package/dist/Modal.d.ts +3 -0
  127. package/dist/Modal.js +127 -0
  128. package/dist/Modal.js.map +1 -0
  129. package/dist/ModalSheet.d.ts +6 -0
  130. package/dist/ModalSheet.js +42 -0
  131. package/dist/ModalSheet.js.map +1 -0
  132. package/dist/MultiselectField.d.ts +3 -0
  133. package/dist/MultiselectField.js +45 -0
  134. package/dist/MultiselectField.js.map +1 -0
  135. package/dist/NumberField.d.ts +3 -0
  136. package/dist/NumberField.js +60 -0
  137. package/dist/NumberField.js.map +1 -0
  138. package/dist/NumberPickerActionSheet.d.ts +7 -0
  139. package/dist/NumberPickerActionSheet.js +22 -0
  140. package/dist/NumberPickerActionSheet.js.map +1 -0
  141. package/dist/OpenAPIContext.d.ts +4 -0
  142. package/dist/OpenAPIContext.js +53 -0
  143. package/dist/OpenAPIContext.js.map +1 -0
  144. package/dist/Page.d.ts +7 -0
  145. package/dist/Page.js +24 -0
  146. package/dist/Page.js.map +1 -0
  147. package/dist/Pagination.d.ts +3 -0
  148. package/dist/Pagination.js +106 -0
  149. package/dist/Pagination.js.map +1 -0
  150. package/dist/PasswordField.d.ts +2 -0
  151. package/dist/PasswordField.js +6 -0
  152. package/dist/PasswordField.js.map +1 -0
  153. package/dist/Permissions.d.ts +2 -0
  154. package/dist/Permissions.js +35 -0
  155. package/dist/Permissions.js.map +1 -0
  156. package/dist/PhoneNumberField.d.ts +3 -0
  157. package/dist/PhoneNumberField.js +83 -0
  158. package/dist/PhoneNumberField.js.map +1 -0
  159. package/dist/PickerSelect.d.ts +46 -0
  160. package/dist/PickerSelect.js +306 -0
  161. package/dist/PickerSelect.js.map +1 -0
  162. package/dist/Radio.d.ts +3 -0
  163. package/dist/Radio.js +21 -0
  164. package/dist/Radio.js.map +1 -0
  165. package/dist/RadioField.d.ts +3 -0
  166. package/dist/RadioField.js +16 -0
  167. package/dist/RadioField.js.map +1 -0
  168. package/dist/ScrollView.d.ts +2 -0
  169. package/dist/ScrollView.js +3 -0
  170. package/dist/ScrollView.js.map +1 -0
  171. package/dist/SectionDivider.d.ts +2 -0
  172. package/dist/SectionDivider.js +12 -0
  173. package/dist/SectionDivider.js.map +1 -0
  174. package/dist/SegmentedControl.d.ts +3 -0
  175. package/dist/SegmentedControl.js +65 -0
  176. package/dist/SegmentedControl.js.map +1 -0
  177. package/dist/SelectBadge.d.ts +3 -0
  178. package/dist/SelectBadge.js +166 -0
  179. package/dist/SelectBadge.js.map +1 -0
  180. package/dist/SelectField.d.ts +3 -0
  181. package/dist/SelectField.js +16 -0
  182. package/dist/SelectField.js.map +1 -0
  183. package/dist/SideDrawer.d.ts +3 -0
  184. package/dist/SideDrawer.js +32 -0
  185. package/dist/SideDrawer.js.map +1 -0
  186. package/dist/Signature.d.ts +8 -0
  187. package/dist/Signature.js +21 -0
  188. package/dist/Signature.js.map +1 -0
  189. package/dist/Signature.native.d.ts +8 -0
  190. package/dist/Signature.native.js +26 -0
  191. package/dist/Signature.native.js.map +1 -0
  192. package/dist/SignatureField.d.ts +3 -0
  193. package/dist/SignatureField.js +42 -0
  194. package/dist/SignatureField.js.map +1 -0
  195. package/dist/Slider.d.ts +3 -0
  196. package/dist/Slider.js +78 -0
  197. package/dist/Slider.js.map +1 -0
  198. package/dist/Spinner.d.ts +3 -0
  199. package/dist/Spinner.js +33 -0
  200. package/dist/Spinner.js.map +1 -0
  201. package/dist/SplitPage.d.ts +2 -0
  202. package/dist/SplitPage.js +139 -0
  203. package/dist/SplitPage.js.map +1 -0
  204. package/dist/SplitPage.native.d.ts +2 -0
  205. package/dist/SplitPage.native.js +75 -0
  206. package/dist/SplitPage.native.js.map +1 -0
  207. package/dist/TapToEdit.d.ts +4 -0
  208. package/dist/TapToEdit.js +170 -0
  209. package/dist/TapToEdit.js.map +1 -0
  210. package/dist/TerrenoProvider.d.ts +6 -0
  211. package/dist/TerrenoProvider.js +10 -0
  212. package/dist/TerrenoProvider.js.map +1 -0
  213. package/dist/Text.d.ts +3 -0
  214. package/dist/Text.js +95 -0
  215. package/dist/Text.js.map +1 -0
  216. package/dist/TextArea.d.ts +3 -0
  217. package/dist/TextArea.js +6 -0
  218. package/dist/TextArea.js.map +1 -0
  219. package/dist/TextField.d.ts +3 -0
  220. package/dist/TextField.js +144 -0
  221. package/dist/TextField.js.map +1 -0
  222. package/dist/TextFieldNumberActionSheet.d.ts +7 -0
  223. package/dist/TextFieldNumberActionSheet.js +20 -0
  224. package/dist/TextFieldNumberActionSheet.js.map +1 -0
  225. package/dist/Theme.d.ts +96 -0
  226. package/dist/Theme.js +213 -0
  227. package/dist/Theme.js.map +1 -0
  228. package/dist/TimezonePicker.d.ts +11 -0
  229. package/dist/TimezonePicker.js +27 -0
  230. package/dist/TimezonePicker.js.map +1 -0
  231. package/dist/Toast.d.ts +23 -0
  232. package/dist/Toast.js +157 -0
  233. package/dist/Toast.js.map +1 -0
  234. package/dist/Tooltip.d.ts +3 -0
  235. package/dist/Tooltip.js +289 -0
  236. package/dist/Tooltip.js.map +1 -0
  237. package/dist/UnifiedAddressAutoComplete.d.ts +2 -0
  238. package/dist/UnifiedAddressAutoComplete.js +23 -0
  239. package/dist/UnifiedAddressAutoComplete.js.map +1 -0
  240. package/dist/Unifier.d.ts +43 -0
  241. package/dist/Unifier.js +154 -0
  242. package/dist/Unifier.js.map +1 -0
  243. package/dist/Utilities.d.ts +56 -0
  244. package/dist/Utilities.js +193 -0
  245. package/dist/Utilities.js.map +1 -0
  246. package/dist/WebAddressAutocomplete.d.ts +3 -0
  247. package/dist/WebAddressAutocomplete.js +61 -0
  248. package/dist/WebAddressAutocomplete.js.map +1 -0
  249. package/dist/fieldElements/FieldError.d.ts +6 -0
  250. package/dist/fieldElements/FieldError.js +9 -0
  251. package/dist/fieldElements/FieldError.js.map +1 -0
  252. package/dist/fieldElements/FieldHelperText.d.ts +6 -0
  253. package/dist/fieldElements/FieldHelperText.js +8 -0
  254. package/dist/fieldElements/FieldHelperText.js.map +1 -0
  255. package/dist/fieldElements/FieldTitle.d.ts +6 -0
  256. package/dist/fieldElements/FieldTitle.js +16 -0
  257. package/dist/fieldElements/FieldTitle.js.map +1 -0
  258. package/dist/fieldElements/index.d.ts +3 -0
  259. package/dist/fieldElements/index.js +4 -0
  260. package/dist/fieldElements/index.js.map +1 -0
  261. package/dist/icons/MobileIcon.d.ts +2 -0
  262. package/dist/icons/MobileIcon.js +18 -0
  263. package/dist/icons/MobileIcon.js.map +1 -0
  264. package/dist/icons/OfflineIcon.d.ts +2 -0
  265. package/dist/icons/OfflineIcon.js +18 -0
  266. package/dist/icons/OfflineIcon.js.map +1 -0
  267. package/dist/icons/OnlineIcon.d.ts +2 -0
  268. package/dist/icons/OnlineIcon.js +19 -0
  269. package/dist/icons/OnlineIcon.js.map +1 -0
  270. package/dist/icons/OutOfficeIcon.d.ts +2 -0
  271. package/dist/icons/OutOfficeIcon.js +18 -0
  272. package/dist/icons/OutOfficeIcon.js.map +1 -0
  273. package/dist/icons/index.d.ts +4 -0
  274. package/dist/icons/index.js +5 -0
  275. package/dist/icons/index.js.map +1 -0
  276. package/dist/index.d.ts +1328 -0
  277. package/dist/index.js +89 -0
  278. package/dist/index.js.map +1 -0
  279. package/dist/table/Table.d.ts +3 -0
  280. package/dist/table/Table.js +53 -0
  281. package/dist/table/Table.js.map +1 -0
  282. package/dist/table/TableBadge.d.ts +6 -0
  283. package/dist/table/TableBadge.js +23 -0
  284. package/dist/table/TableBadge.js.map +1 -0
  285. package/dist/table/TableBoolean.d.ts +6 -0
  286. package/dist/table/TableBoolean.js +37 -0
  287. package/dist/table/TableBoolean.js.map +1 -0
  288. package/dist/table/TableDate.d.ts +3 -0
  289. package/dist/table/TableDate.js +27 -0
  290. package/dist/table/TableDate.js.map +1 -0
  291. package/dist/table/TableHeader.d.ts +6 -0
  292. package/dist/table/TableHeader.js +10 -0
  293. package/dist/table/TableHeader.js.map +1 -0
  294. package/dist/table/TableHeaderCell.d.ts +6 -0
  295. package/dist/table/TableHeaderCell.js +54 -0
  296. package/dist/table/TableHeaderCell.js.map +1 -0
  297. package/dist/table/TableIconButton.d.ts +3 -0
  298. package/dist/table/TableIconButton.js +39 -0
  299. package/dist/table/TableIconButton.js.map +1 -0
  300. package/dist/table/TableNumber.d.ts +3 -0
  301. package/dist/table/TableNumber.js +18 -0
  302. package/dist/table/TableNumber.js.map +1 -0
  303. package/dist/table/TableRow.d.ts +6 -0
  304. package/dist/table/TableRow.js +22 -0
  305. package/dist/table/TableRow.js.map +1 -0
  306. package/dist/table/TableText.d.ts +3 -0
  307. package/dist/table/TableText.js +18 -0
  308. package/dist/table/TableText.js.map +1 -0
  309. package/dist/table/TableTitle.d.ts +3 -0
  310. package/dist/table/TableTitle.js +21 -0
  311. package/dist/table/TableTitle.js.map +1 -0
  312. package/dist/table/tableContext.d.ts +5 -0
  313. package/dist/table/tableContext.js +38 -0
  314. package/dist/table/tableContext.js.map +1 -0
  315. package/dist/useStoredState.d.ts +1 -0
  316. package/dist/useStoredState.js +49 -0
  317. package/dist/useStoredState.js.map +1 -0
  318. package/package.json +123 -0
  319. package/src/Accordion.test.tsx +104 -0
  320. package/src/Accordion.tsx +81 -0
  321. package/src/ActionSheet.tsx +881 -0
  322. package/src/AddressField.test.tsx +120 -0
  323. package/src/AddressField.tsx +122 -0
  324. package/src/Avatar.test.tsx +163 -0
  325. package/src/Avatar.tsx +298 -0
  326. package/src/Badge.test.tsx +116 -0
  327. package/src/Badge.tsx +136 -0
  328. package/src/Banner.tsx +200 -0
  329. package/src/Body.tsx +34 -0
  330. package/src/BooleanField.tsx +141 -0
  331. package/src/Box.test.tsx +662 -0
  332. package/src/Box.tsx +368 -0
  333. package/src/Button.tsx +196 -0
  334. package/src/Card.tsx +19 -0
  335. package/src/CheckBox.tsx +45 -0
  336. package/src/Common.ts +2787 -0
  337. package/src/CommonIconTypes.ts +2030 -0
  338. package/src/Constants.ts +3311 -0
  339. package/src/CustomSelectField.tsx +115 -0
  340. package/src/DataTable.tsx +674 -0
  341. package/src/DateTimeActionSheet.tsx +559 -0
  342. package/src/DateTimeField.test.tsx +393 -0
  343. package/src/DateTimeField.tsx +777 -0
  344. package/src/DateUtilities.test.ts +440 -0
  345. package/src/DateUtilities.tsx +370 -0
  346. package/src/DecimalRangeActionSheet.tsx +85 -0
  347. package/src/DismissButton.tsx +31 -0
  348. package/src/EmailField.tsx +66 -0
  349. package/src/EmojiSelector.test.tsx +61 -0
  350. package/src/EmojiSelector.tsx +510 -0
  351. package/src/ErrorBoundary.tsx +37 -0
  352. package/src/ErrorPage.tsx +41 -0
  353. package/src/Field.tsx +101 -0
  354. package/src/FlatList.tsx +2 -0
  355. package/src/Heading.tsx +66 -0
  356. package/src/HeightActionSheet.tsx +91 -0
  357. package/src/Hyperlink.tsx +179 -0
  358. package/src/Icon.tsx +36 -0
  359. package/src/IconButton.tsx +217 -0
  360. package/src/Image.tsx +51 -0
  361. package/src/ImageBackground.tsx +14 -0
  362. package/src/InfoModalIcon.tsx +42 -0
  363. package/src/InfoTooltipButton.tsx +16 -0
  364. package/src/Link.tsx +22 -0
  365. package/src/MarkdownView.tsx +67 -0
  366. package/src/MediaQuery.ts +46 -0
  367. package/src/MobileAddressAutoComplete.tsx +126 -0
  368. package/src/Modal.tsx +300 -0
  369. package/src/ModalSheet.tsx +58 -0
  370. package/src/MultiselectField.tsx +112 -0
  371. package/src/NumberField.tsx +67 -0
  372. package/src/NumberPickerActionSheet.tsx +51 -0
  373. package/src/OpenAPIContext.tsx +74 -0
  374. package/src/Page.tsx +105 -0
  375. package/src/Pagination.tsx +169 -0
  376. package/src/PasswordField.tsx +7 -0
  377. package/src/Permissions.ts +43 -0
  378. package/src/PhoneNumberField.tsx +109 -0
  379. package/src/PickerSelect.tsx +571 -0
  380. package/src/Radio.tsx +33 -0
  381. package/src/RadioField.tsx +43 -0
  382. package/src/ScrollView.tsx +2 -0
  383. package/src/SectionDivider.tsx +18 -0
  384. package/src/SegmentedControl.tsx +126 -0
  385. package/src/SelectBadge.tsx +280 -0
  386. package/src/SelectField.tsx +41 -0
  387. package/src/SideDrawer.tsx +56 -0
  388. package/src/Signature.native.tsx +57 -0
  389. package/src/Signature.tsx +44 -0
  390. package/src/SignatureField.tsx +92 -0
  391. package/src/Slider.tsx +199 -0
  392. package/src/Spinner.tsx +35 -0
  393. package/src/SplitPage.native.tsx +163 -0
  394. package/src/SplitPage.tsx +304 -0
  395. package/src/TapToEdit.tsx +292 -0
  396. package/src/TerrenoProvider.tsx +31 -0
  397. package/src/Text.tsx +123 -0
  398. package/src/TextArea.test.tsx +255 -0
  399. package/src/TextArea.tsx +8 -0
  400. package/src/TextField.test.tsx +487 -0
  401. package/src/TextField.tsx +260 -0
  402. package/src/TextFieldNumberActionSheet.tsx +46 -0
  403. package/src/Theme.tsx +248 -0
  404. package/src/TimezonePicker.tsx +45 -0
  405. package/src/Toast.tsx +234 -0
  406. package/src/Tooltip.tsx +407 -0
  407. package/src/UnifiedAddressAutoComplete.tsx +66 -0
  408. package/src/Unifier.ts +172 -0
  409. package/src/Utilities.tsx +329 -0
  410. package/src/WebAddressAutocomplete.tsx +84 -0
  411. package/src/__snapshots__/Accordion.test.tsx.snap +126 -0
  412. package/src/__snapshots__/AddressField.test.tsx.snap +1197 -0
  413. package/src/__snapshots__/Avatar.test.tsx.snap +57 -0
  414. package/src/__snapshots__/Badge.test.tsx.snap +55 -0
  415. package/src/__snapshots__/Box.test.tsx.snap +162 -0
  416. package/src/__snapshots__/EmojiSelector.test.tsx.snap +422 -0
  417. package/src/__snapshots__/TextArea.test.tsx.snap +521 -0
  418. package/src/__snapshots__/TextField.test.tsx.snap +569 -0
  419. package/src/bunSetup.ts +1235 -0
  420. package/src/fieldElements/FieldError.tsx +24 -0
  421. package/src/fieldElements/FieldHelperText.tsx +20 -0
  422. package/src/fieldElements/FieldTitle.tsx +31 -0
  423. package/src/fieldElements/index.tsx +3 -0
  424. package/src/icons/MobileIcon.tsx +40 -0
  425. package/src/icons/OfflineIcon.tsx +37 -0
  426. package/src/icons/OnlineIcon.tsx +39 -0
  427. package/src/icons/OutOfficeIcon.tsx +36 -0
  428. package/src/icons/index.ts +4 -0
  429. package/src/index.tsx +1375 -0
  430. package/src/polyfill.d.ts +11 -0
  431. package/src/table/Table.tsx +109 -0
  432. package/src/table/TableBadge.tsx +46 -0
  433. package/src/table/TableBoolean.tsx +70 -0
  434. package/src/table/TableDate.tsx +38 -0
  435. package/src/table/TableHeader.tsx +20 -0
  436. package/src/table/TableHeaderCell.tsx +94 -0
  437. package/src/table/TableIconButton.tsx +61 -0
  438. package/src/table/TableNumber.tsx +29 -0
  439. package/src/table/TableRow.tsx +67 -0
  440. package/src/table/TableText.tsx +29 -0
  441. package/src/table/TableTitle.tsx +31 -0
  442. package/src/table/tableContext.tsx +67 -0
  443. package/src/test-utils.tsx +27 -0
  444. package/src/types/react-native-swiper-flatlist.d.ts +56 -0
  445. package/src/useStoredState.test.tsx +143 -0
  446. package/src/useStoredState.ts +56 -0
@@ -0,0 +1,777 @@
1
+ import {DateTime} from "luxon";
2
+ import React, {type FC, useCallback, useEffect, useRef, useState} from "react";
3
+ import {TextInput, View} from "react-native";
4
+
5
+ import {Box} from "./Box";
6
+ import type {DateTimeFieldProps, IconName} from "./Common";
7
+ import {DateTimeActionSheet} from "./DateTimeActionSheet";
8
+ import {FieldError, FieldHelperText, FieldTitle} from "./fieldElements";
9
+ import {IconButton} from "./IconButton";
10
+ import {isMobileDevice} from "./MediaQuery";
11
+ import {SelectField} from "./SelectField";
12
+ import {Text} from "./Text";
13
+ import {useTheme} from "./Theme";
14
+ import {TimezonePicker} from "./TimezonePicker";
15
+
16
+ interface SeparatorProps {
17
+ type: "date" | "time";
18
+ }
19
+
20
+ const Separator: FC<SeparatorProps> = ({type}) => {
21
+ return (
22
+ <View>
23
+ <Text>{type === "time" ? ":" : "/"}</Text>
24
+ </View>
25
+ );
26
+ };
27
+
28
+ interface DateTimeSegmentProps {
29
+ config: FieldConfig;
30
+ disabled?: boolean;
31
+ getFieldValue: (index: number) => string;
32
+ handleFieldChange: (index: number, text: string, config: FieldConfig) => void;
33
+ onBlur: (override?: {amPm?: "am" | "pm"; timezone?: string}) => void;
34
+ onRef: (ref: TextInput | null, index: number) => void;
35
+ index: number;
36
+ error?: string;
37
+ }
38
+
39
+ const DateTimeSegment: FC<DateTimeSegmentProps> = ({
40
+ disabled,
41
+ getFieldValue,
42
+ handleFieldChange,
43
+ onBlur,
44
+ onRef,
45
+ index,
46
+ config,
47
+ error,
48
+ }): React.ReactElement => {
49
+ const {theme} = useTheme();
50
+ return (
51
+ <View
52
+ style={{
53
+ alignItems: "center",
54
+ backgroundColor: "transparent",
55
+ borderColor: error ? theme.border.error : "transparent",
56
+ flexDirection: "row",
57
+ flexShrink: 1,
58
+ height: 50,
59
+ overflow: "hidden",
60
+ padding: 0,
61
+ width: config.width,
62
+ }}
63
+ >
64
+ <TextInput
65
+ accessibilityHint={`Enter the ${config.placeholder}`}
66
+ aria-label="Text input field"
67
+ inputMode="numeric"
68
+ onBlur={() => onBlur()}
69
+ onChangeText={(text) => {
70
+ handleFieldChange(index, text, config);
71
+ }}
72
+ placeholder={config.placeholder}
73
+ readOnly={disabled}
74
+ ref={(el) => onRef(el, index)}
75
+ selectTextOnFocus
76
+ style={{
77
+ color: error ? theme.text.error : theme.text.primary,
78
+ textAlign: "center",
79
+ width: config.width - 2,
80
+ }}
81
+ value={getFieldValue(index)}
82
+ />
83
+ </View>
84
+ );
85
+ };
86
+
87
+ interface DateTimeProps extends Omit<DateTimeSegmentProps, "index" | "config"> {
88
+ fieldConfigs: FieldConfig[];
89
+ type: "date" | "datetime" | "time";
90
+ fieldErrors?: Record<number, string | undefined>;
91
+ }
92
+
93
+ const DateField: FC<DateTimeProps> = ({fieldErrors, ...segmentProps}) => {
94
+ return (
95
+ <View
96
+ style={{
97
+ alignItems: "center",
98
+ flexDirection: "row",
99
+ justifyContent: "space-between",
100
+ width: 130,
101
+ }}
102
+ >
103
+ <DateTimeSegment
104
+ {...segmentProps}
105
+ config={segmentProps.fieldConfigs[0]}
106
+ error={fieldErrors?.[0]}
107
+ index={0}
108
+ />
109
+ <Separator type="date" />
110
+ <DateTimeSegment
111
+ {...segmentProps}
112
+ config={segmentProps.fieldConfigs[1]}
113
+ error={fieldErrors?.[1]}
114
+ index={1}
115
+ />
116
+ <Separator type="date" />
117
+ <DateTimeSegment
118
+ {...segmentProps}
119
+ config={segmentProps.fieldConfigs[2]}
120
+ error={fieldErrors?.[2]}
121
+ index={2}
122
+ />
123
+ </View>
124
+ );
125
+ };
126
+
127
+ const TimeField: FC<DateTimeProps> = ({type, onBlur, fieldErrors, ...segmentProps}) => {
128
+ const hourIndex = type === "time" ? 0 : 3;
129
+ const minuteIndex = type === "time" ? 1 : 4;
130
+ return (
131
+ <View style={{alignItems: "center", flexDirection: "row", width: 65}}>
132
+ <DateTimeSegment
133
+ {...segmentProps}
134
+ config={segmentProps.fieldConfigs[hourIndex]}
135
+ error={fieldErrors?.[hourIndex]}
136
+ index={hourIndex}
137
+ onBlur={onBlur}
138
+ />
139
+ <Separator type="time" />
140
+ <DateTimeSegment
141
+ {...segmentProps}
142
+ config={segmentProps.fieldConfigs[minuteIndex]}
143
+ error={fieldErrors?.[minuteIndex]}
144
+ index={minuteIndex}
145
+ onBlur={onBlur}
146
+ />
147
+ </View>
148
+ );
149
+ };
150
+
151
+ interface FieldConfig {
152
+ maxLength: number;
153
+ placeholder: string;
154
+ width: number;
155
+ }
156
+
157
+ export const DateTimeField: FC<DateTimeFieldProps> = ({
158
+ type,
159
+ title,
160
+ value,
161
+ onChange,
162
+ timezone: providedTimezone,
163
+ onTimezoneChange,
164
+ errorText,
165
+ disabled,
166
+ helperText,
167
+ }): React.ReactElement => {
168
+ const {theme} = useTheme();
169
+ const dateActionSheetRef: React.RefObject<any> = React.createRef();
170
+ const [amPm, setAmPm] = useState<"am" | "pm">("am");
171
+ const [showDate, setShowDate] = useState(false);
172
+ const [month, setMonth] = useState("");
173
+ const [day, setDay] = useState("");
174
+ const [year, setYear] = useState("");
175
+ const [hour, setHour] = useState("");
176
+ const [minute, setMinute] = useState("");
177
+ const [fieldErrors, setFieldErrors] = useState<Record<number, string | undefined>>({});
178
+ const [localTimezone, setLocalTimezone] = useState(
179
+ providedTimezone ?? DateTime.local().zoneName ?? "UTC"
180
+ );
181
+
182
+ const breakpoint = 395; // Breakpoint for switching to action sheet
183
+ let minimumWidth = 230; // Minimum width for the field container
184
+ if (type === "date") {
185
+ minimumWidth = 200;
186
+ }
187
+
188
+ let maximumWidth = breakpoint; // Maximum width for the field container
189
+ if (["date", "time"].includes(type)) {
190
+ maximumWidth = minimumWidth + 10;
191
+ }
192
+
193
+ const [parentWidth, setParentWidth] = useState<number | null>(null);
194
+ const parentIsLessThanBreakpointOrIsMobile =
195
+ (parentWidth !== null && parentWidth < breakpoint) || isMobileDevice();
196
+
197
+ // We need to store the pending value in a ref because the state changes don't trigger
198
+ // immediately, so onBlur may use stale values.
199
+ const pendingValueRef = useRef<
200
+ | {
201
+ amPm?: "am" | "pm";
202
+ timezone?: string;
203
+ minute?: string;
204
+ month?: string;
205
+ day?: string;
206
+ year?: string;
207
+ hour?: string;
208
+ }
209
+ | undefined
210
+ >(undefined);
211
+
212
+ // Use provided timezone if available, otherwise use local
213
+ const timezone = providedTimezone ?? localTimezone;
214
+ const lastTimezoneRef = useRef(timezone);
215
+
216
+ const inputRefs = useRef<(TextInput | null)[]>([]);
217
+
218
+ let iconName: IconName | undefined;
219
+ if (disabled) {
220
+ iconName = undefined;
221
+ } else if (type === "time") {
222
+ iconName = "clock";
223
+ } else {
224
+ iconName = "calendar";
225
+ }
226
+
227
+ let borderColor = theme.border.dark;
228
+ if (disabled) {
229
+ borderColor = theme.border.activeNeutral;
230
+ } else if (errorText || Object.values(fieldErrors).some((error) => error !== undefined)) {
231
+ borderColor = theme.border.error;
232
+ }
233
+
234
+ const getFieldConfigs = useCallback((): FieldConfig[] => {
235
+ const configs: FieldConfig[] = [];
236
+ if (type === "date" || type === "datetime") {
237
+ configs.push(
238
+ {maxLength: 2, placeholder: "MM", width: 40},
239
+ {maxLength: 2, placeholder: "DD", width: 30},
240
+ {maxLength: 4, placeholder: "YYYY", width: 50}
241
+ );
242
+ }
243
+ if (type === "time" || type === "datetime") {
244
+ configs.push(
245
+ {maxLength: 2, placeholder: "hh", width: 30},
246
+ {maxLength: 2, placeholder: "mm", width: 30}
247
+ );
248
+ }
249
+ return configs;
250
+ }, [type]);
251
+
252
+ // Set the inputRefs array to the correct length
253
+ useEffect(() => {
254
+ const configs = getFieldConfigs();
255
+ inputRefs.current = configs.map(() => null);
256
+ }, [getFieldConfigs]);
257
+
258
+ const validateField = useCallback(
259
+ (fieldIndex: number, fieldValue: string): string | undefined => {
260
+ if (!fieldValue) return undefined;
261
+
262
+ if (type === "date" || type === "datetime") {
263
+ if (fieldIndex === 0) {
264
+ // Month
265
+ const monthNum = parseInt(fieldValue, 10);
266
+ if (Number.isNaN(monthNum) || monthNum < 1 || monthNum > 12) {
267
+ return "Month must be between 1 and 12";
268
+ }
269
+ } else if (fieldIndex === 1) {
270
+ // Day
271
+ const dayNum = parseInt(fieldValue, 10);
272
+ if (Number.isNaN(dayNum) || dayNum < 1 || dayNum > 31) {
273
+ return "Day must be between 1 and 31";
274
+ }
275
+ } else if (fieldIndex === 2) {
276
+ // Year
277
+ const yearNum = parseInt(fieldValue, 10);
278
+ if (Number.isNaN(yearNum) || yearNum < 1900 || yearNum > 2100) {
279
+ return "Year must be between 1900 and 2100";
280
+ }
281
+ }
282
+ }
283
+
284
+ if (type === "time" || type === "datetime") {
285
+ if (fieldIndex === (type === "time" ? 0 : 3)) {
286
+ // Hour
287
+ const hourNum = parseInt(fieldValue, 10);
288
+ if (Number.isNaN(hourNum) || hourNum < 1 || hourNum > 12) {
289
+ return "Hour must be between 1 and 12";
290
+ }
291
+ } else if (fieldIndex === (type === "time" ? 1 : 4)) {
292
+ // Minute
293
+ const minuteNum = parseInt(fieldValue, 10);
294
+ if (Number.isNaN(minuteNum) || minuteNum < 0 || minuteNum > 59) {
295
+ return "Minute must be between 0 and 59";
296
+ }
297
+ }
298
+ }
299
+
300
+ return undefined;
301
+ },
302
+ [type]
303
+ );
304
+
305
+ const getISOFromFields = useCallback(
306
+ (override?: {
307
+ amPm?: "am" | "pm";
308
+ timezone?: string;
309
+ minute?: string;
310
+ month?: string;
311
+ day?: string;
312
+ year?: string;
313
+ hour?: string;
314
+ }): string | undefined => {
315
+ const ampPmVal = override?.amPm ?? amPm;
316
+ const minuteVal = override?.minute ?? minute;
317
+ const monthVal = override?.month ?? month;
318
+ const dayVal = override?.day ?? day;
319
+ const yearVal = override?.year ?? year;
320
+ const hourVal = override?.hour ?? hour;
321
+ let date;
322
+ if (type === "datetime") {
323
+ if (!monthVal || !dayVal || !yearVal || !hour || !minuteVal) {
324
+ return undefined;
325
+ }
326
+ let hourNum = parseInt(hourVal, 10);
327
+ if (ampPmVal === "pm" && hourNum !== 12) {
328
+ hourNum += 12;
329
+ } else if (ampPmVal === "am" && hourNum === 12) {
330
+ hourNum = 0;
331
+ }
332
+ date = DateTime.fromObject(
333
+ {
334
+ day: parseInt(dayVal, 10),
335
+ hour: hourNum,
336
+ millisecond: 0,
337
+ minute: parseInt(minuteVal, 10),
338
+ month: parseInt(monthVal, 10),
339
+ second: 0,
340
+ year: parseInt(yearVal, 10),
341
+ },
342
+ {
343
+ zone: override?.timezone ?? timezone,
344
+ }
345
+ );
346
+ } else if (type === "date") {
347
+ if (!monthVal || !dayVal || !yearVal) {
348
+ return undefined;
349
+ }
350
+ date = DateTime.fromObject(
351
+ {
352
+ day: parseInt(dayVal, 10),
353
+ hour: 0,
354
+ millisecond: 0,
355
+ minute: 0,
356
+ month: parseInt(monthVal, 10),
357
+ second: 0,
358
+ year: parseInt(yearVal, 10),
359
+ },
360
+ {
361
+ zone: "UTC",
362
+ }
363
+ );
364
+ } else {
365
+ if (!hour || !minuteVal) {
366
+ return undefined;
367
+ }
368
+ let hourNum = parseInt(hour, 10);
369
+ if (ampPmVal === "pm" && hourNum !== 12) {
370
+ hourNum += 12;
371
+ } else if (ampPmVal === "am" && hourNum === 12) {
372
+ hourNum = 0;
373
+ }
374
+ date = DateTime.fromObject(
375
+ {
376
+ hour: hourNum,
377
+ millisecond: 0,
378
+ minute: parseInt(minuteVal, 10),
379
+ second: 0,
380
+ },
381
+ {
382
+ zone: override?.timezone ?? timezone,
383
+ }
384
+ );
385
+ }
386
+
387
+ if (date.isValid) {
388
+ // Always return UTC ISO string
389
+ return date.toUTC().toISO();
390
+ }
391
+ return undefined;
392
+ },
393
+ [amPm, month, day, year, hour, minute, timezone, type]
394
+ );
395
+
396
+ const handleFieldChange = useCallback(
397
+ (index: number, text: string, config: FieldConfig) => {
398
+ const numericValue = text.replace(/[^0-9]/g, "");
399
+
400
+ // For minutes, just ensure it's at most 2 digits and valid (0-59)
401
+ if ((type === "time" && index === 1) || (type === "datetime" && index === 4)) {
402
+ // For minutes, keep only the last two digits entered.
403
+ // If the user deletes everything, set the value to "00"
404
+ // so it's always a valid time and easier to edit.
405
+ // This lets users freely edit or clear the minute field without breaking the time format.
406
+ const finalValue = numericValue === "" ? "00" : numericValue.slice(-2);
407
+ const minuteNum = parseInt(finalValue, 10);
408
+
409
+ // Update the minute state so the UI reflects the latest input,
410
+ // even if it's temporarily invalid
411
+ // This allows the user to freely edit or clear the field.
412
+ setMinute(finalValue);
413
+
414
+ // Only update ref and result if it's a valid minute value
415
+ if (!Number.isNaN(minuteNum) && minuteNum >= 0 && minuteNum <= 59) {
416
+ pendingValueRef.current = {minute: finalValue};
417
+ setFieldErrors((prev) => ({...prev, [index]: undefined}));
418
+
419
+ // Pass the new minute value directly to getISOFromFields
420
+ const result = getISOFromFields({minute: finalValue});
421
+ if (result) {
422
+ const currentValueUTC = value ? DateTime.fromISO(value).toUTC().toISO() : undefined;
423
+ if (result !== currentValueUTC) {
424
+ onChange(result);
425
+ }
426
+ }
427
+ } else {
428
+ setFieldErrors((prev) => ({...prev, [index]: "Minute must be between 0 and 59"}));
429
+ }
430
+
431
+ // Auto-advance to next field if current field is full
432
+ const configs = getFieldConfigs();
433
+ if (finalValue.length === config.maxLength && index < configs.length - 1) {
434
+ inputRefs.current[index + 1]?.focus();
435
+ }
436
+ return;
437
+ }
438
+
439
+ // For other fields, handle leading zeros
440
+ const finalValue =
441
+ numericValue.length > config.maxLength
442
+ ? numericValue.slice(-config.maxLength)
443
+ : numericValue;
444
+
445
+ const error = validateField(index, finalValue);
446
+ setFieldErrors((prev) => ({...prev, [index]: error}));
447
+
448
+ if (type === "date" || type === "datetime") {
449
+ if (index === 0) {
450
+ setMonth(finalValue);
451
+ pendingValueRef.current = {month: finalValue};
452
+ }
453
+ if (index === 1) {
454
+ setDay(finalValue);
455
+ pendingValueRef.current = {day: finalValue};
456
+ }
457
+ if (index === 2) {
458
+ setYear(finalValue);
459
+ pendingValueRef.current = {year: finalValue};
460
+ }
461
+ }
462
+
463
+ if (type === "time") {
464
+ if (index === 0) {
465
+ setHour(finalValue);
466
+ pendingValueRef.current = {hour: finalValue};
467
+ }
468
+ }
469
+
470
+ if (type === "datetime") {
471
+ if (index === 3) {
472
+ setHour(finalValue);
473
+ pendingValueRef.current = {hour: finalValue};
474
+ }
475
+ }
476
+
477
+ // If date parts are complete and valid, emit ISO immediately (don't wait for blur)
478
+ if ((type === "date" || type === "datetime") && !error) {
479
+ const monthVal = index === 0 ? finalValue : month;
480
+ const dayVal = index === 1 ? finalValue : day;
481
+ const yearVal = index === 2 ? finalValue : year;
482
+ const monthComplete = monthVal?.length === 2;
483
+ const dayComplete = dayVal?.length === 2;
484
+ const yearComplete = yearVal?.length === 4;
485
+ const haveAllDateParts = monthComplete && dayComplete && yearComplete;
486
+ if (haveAllDateParts) {
487
+ const result = getISOFromFields({day: dayVal, month: monthVal, year: yearVal});
488
+ if (result) {
489
+ const currentValueUTC = value ? DateTime.fromISO(value).toUTC().toISO() : undefined;
490
+ if (result !== currentValueUTC) {
491
+ onChange(result);
492
+ }
493
+ }
494
+ }
495
+ }
496
+
497
+ // Auto-advance to next field if current field is full
498
+ const configs = getFieldConfigs();
499
+ if (finalValue.length === config.maxLength && index < configs.length - 1) {
500
+ inputRefs.current[index + 1]?.focus();
501
+ }
502
+ },
503
+ [type, getFieldConfigs, getISOFromFields, onChange, value, validateField, month, day, year]
504
+ );
505
+
506
+ const onActionSheetChange = useCallback(
507
+ (inputDate: string) => {
508
+ // Handle clear case - empty string should clear the field
509
+ if (!inputDate || inputDate === "") {
510
+ onChange("");
511
+ setShowDate(false);
512
+ return;
513
+ }
514
+
515
+ const parsedDate = DateTime.fromISO(inputDate);
516
+ if (!parsedDate.isValid) {
517
+ console.warn("Invalid date passed to DateTimeField", inputDate);
518
+ return;
519
+ }
520
+ setAmPm(parsedDate.hour >= 12 ? "pm" : "am");
521
+
522
+ if (type === "date" || type === "datetime") {
523
+ setMonth(parsedDate.month.toString().padStart(2, "0"));
524
+ setDay(parsedDate.day.toString().padStart(2, "0"));
525
+ setYear(parsedDate.year.toString());
526
+ }
527
+
528
+ if (type === "time" || type === "datetime") {
529
+ let hourNum = parsedDate.hour % 12;
530
+ hourNum = hourNum === 0 ? 12 : hourNum;
531
+ setHour(hourNum.toString().padStart(2, "0"));
532
+ setMinute(parsedDate.minute.toString().padStart(2, "0"));
533
+ }
534
+
535
+ // Normalize emitted value to ISO (UTC for date-only)
536
+ const normalized =
537
+ type === "date"
538
+ ? parsedDate
539
+ .setZone("UTC")
540
+ .startOf("day")
541
+ .set({millisecond: 0, second: 0})
542
+ .toUTC()
543
+ .toISO()
544
+ : parsedDate.set({millisecond: 0, second: 0}).toUTC().toISO();
545
+ if (!normalized) {
546
+ console.warn("Invalid date passed to DateTimeField", parsedDate);
547
+ return;
548
+ }
549
+ onChange(normalized);
550
+ setShowDate(false);
551
+ },
552
+ [onChange, type]
553
+ );
554
+
555
+ // When fields change, send the value to onChange
556
+ const onBlur = useCallback(
557
+ (override?: {amPm?: "am" | "pm"}) => {
558
+ const iso = getISOFromFields({...override, ...pendingValueRef.current});
559
+ // Compare in UTC to avoid timezone issues
560
+ const currentValueUTC = value ? DateTime.fromISO(value).toUTC().toISO() : undefined;
561
+ if (iso && iso !== currentValueUTC) {
562
+ onChange(iso);
563
+ }
564
+
565
+ // Clear the pending value after processing
566
+ pendingValueRef.current = undefined;
567
+ },
568
+ [getISOFromFields, onChange, value]
569
+ );
570
+
571
+ // Handle external value changes
572
+ useEffect(() => {
573
+ if (!value) {
574
+ setMonth("");
575
+ setDay("");
576
+ setYear("");
577
+ setHour("");
578
+ setMinute("");
579
+ setAmPm("am");
580
+ return;
581
+ }
582
+
583
+ // // If only timezone changed, don't recalculate fields
584
+ const isOnlyTimezoneChange =
585
+ lastTimezoneRef.current !== timezone &&
586
+ DateTime.fromISO(value).toUTC().toISO() ===
587
+ DateTime.fromISO(value).setZone(timezone).toUTC().toISO();
588
+
589
+ lastTimezoneRef.current = timezone;
590
+
591
+ if (isOnlyTimezoneChange) {
592
+ return;
593
+ }
594
+
595
+ // Handle dates which should have 00:00:00.000Z as the time component, ignore timezones.
596
+ let parsedDate = DateTime.fromISO(value);
597
+ if (type === "date") {
598
+ parsedDate = parsedDate.setZone("UTC");
599
+ } else {
600
+ parsedDate = parsedDate.setZone(timezone);
601
+ }
602
+ if (!parsedDate.isValid) {
603
+ console.warn("Invalid date passed to DateTimeField", value);
604
+ return;
605
+ }
606
+ setAmPm(parsedDate.hour >= 12 ? "pm" : "am");
607
+
608
+ if (type === "date" || type === "datetime") {
609
+ setMonth(parsedDate.month.toString().padStart(2, "0"));
610
+ setDay(parsedDate.day.toString().padStart(2, "0"));
611
+ setYear(parsedDate.year.toString());
612
+ }
613
+
614
+ if (type === "time" || type === "datetime") {
615
+ let hourNum = parsedDate.hour % 12;
616
+ hourNum = hourNum === 0 ? 12 : hourNum;
617
+ setHour(hourNum.toString().padStart(2, "0"));
618
+ setMinute(parsedDate.minute.toString().padStart(2, "0"));
619
+ }
620
+ }, [value, type, timezone]);
621
+
622
+ // JOSH: This is where the infinite loop is happening
623
+ // We update the value of the date according to the zone and then this get triggered
624
+ // and we update the value of the date according to the zone again
625
+ const getFieldValue = useCallback(
626
+ (index: number): string => {
627
+ if (type === "date" || type === "datetime") {
628
+ if (index === 0) return month;
629
+ if (index === 1) return day;
630
+ if (index === 2) return year;
631
+ }
632
+
633
+ if (type === "time") {
634
+ if (index === 0) return hour;
635
+ if (index === 1) return minute;
636
+ }
637
+
638
+ if (type === "datetime") {
639
+ if (index === 3) return hour;
640
+ if (index === 4) return minute;
641
+ }
642
+
643
+ return "";
644
+ },
645
+ [type, month, day, year, hour, minute]
646
+ );
647
+
648
+ const fieldConfigs = getFieldConfigs();
649
+
650
+ const segmentProps = {
651
+ disabled,
652
+ fieldConfigs,
653
+ fieldErrors,
654
+ getFieldValue,
655
+ handleFieldChange,
656
+ onBlur,
657
+ onRef: (el: TextInput | null, i: number) => (inputRefs.current[i] = el),
658
+ };
659
+
660
+ return (
661
+ <>
662
+ {Boolean(title) && <FieldTitle text={title!} />}
663
+ {Boolean(errorText) && <FieldError text={errorText!} />}
664
+ <View
665
+ onLayout={(e) => setParentWidth(e.nativeEvent.layout.width)}
666
+ style={{
667
+ alignItems: "center",
668
+ backgroundColor: theme.surface.base,
669
+ borderColor,
670
+ borderRadius: 4,
671
+ borderWidth: 1,
672
+ flexDirection: parentIsLessThanBreakpointOrIsMobile ? "column" : "row",
673
+ maxWidth: maximumWidth,
674
+ minWidth: minimumWidth,
675
+ paddingHorizontal: 6,
676
+ paddingVertical: 2,
677
+ }}
678
+ >
679
+ {(type === "date" || type === "datetime") && (
680
+ <View style={{alignItems: "center", flexDirection: "row"}}>
681
+ <DateField {...segmentProps} type={type} />
682
+ {!disabled &&
683
+ (type === "date" ||
684
+ (type === "datetime" && parentIsLessThanBreakpointOrIsMobile)) && (
685
+ <IconButton
686
+ accessibilityHint="Opens the calendar to select a date and time"
687
+ accessibilityLabel="Show calendar"
688
+ iconName={iconName!}
689
+ onClick={() => setShowDate(true)}
690
+ variant="muted"
691
+ />
692
+ )}
693
+ </View>
694
+ )}
695
+
696
+ <View style={{alignItems: "center", flexDirection: "row"}}>
697
+ {(type === "time" || type === "datetime") && <TimeField {...segmentProps} type={type} />}
698
+ {Boolean(type === "datetime" || type === "time") && (
699
+ <>
700
+ <Box direction="column" marginLeft={2} marginRight={2} width={60}>
701
+ <SelectField
702
+ disabled={disabled}
703
+ onChange={(result) => {
704
+ setAmPm(result as "am" | "pm");
705
+ // No onblur, so we need to manually update the value
706
+ const iso = getISOFromFields({amPm: result as "am" | "pm"});
707
+ // Compare in UTC to avoid timezone issues
708
+ const currentValueUTC = value
709
+ ? DateTime.fromISO(value).toUTC().toISO()
710
+ : undefined;
711
+ if (iso && iso !== currentValueUTC) {
712
+ onChange(iso);
713
+ }
714
+ }}
715
+ options={[
716
+ {label: "am", value: "am"},
717
+ {label: "pm", value: "pm"},
718
+ ]}
719
+ requireValue
720
+ value={amPm}
721
+ />
722
+ </Box>
723
+ <Box direction="column" width={70}>
724
+ <TimezonePicker
725
+ disabled={disabled}
726
+ hideTitle
727
+ onChange={(t) => {
728
+ if (onTimezoneChange) {
729
+ onTimezoneChange(t);
730
+ } else {
731
+ setLocalTimezone(t);
732
+ }
733
+ const iso = getISOFromFields({timezone: t});
734
+ // Compare in UTC to avoid timezone issues
735
+ const currentValueUTC = value
736
+ ? DateTime.fromISO(value).toUTC().toISO()
737
+ : undefined;
738
+ if (iso && iso !== currentValueUTC) {
739
+ onChange(iso);
740
+ }
741
+ }}
742
+ shortTimezone
743
+ timezone={timezone}
744
+ />
745
+ </Box>
746
+ </>
747
+ )}
748
+
749
+ {!disabled && type === "datetime" && !parentIsLessThanBreakpointOrIsMobile && (
750
+ <Box marginLeft={2}>
751
+ <IconButton
752
+ accessibilityHint="Opens the calendar to select a date and time"
753
+ accessibilityLabel="Show calendar"
754
+ iconName={iconName!}
755
+ onClick={() => setShowDate(true)}
756
+ variant="muted"
757
+ />
758
+ </Box>
759
+ )}
760
+ </View>
761
+ </View>
762
+
763
+ {!disabled && (
764
+ <DateTimeActionSheet
765
+ actionSheetRef={dateActionSheetRef}
766
+ onChange={onActionSheetChange}
767
+ onDismiss={() => setShowDate(false)}
768
+ timezone={timezone}
769
+ type={type}
770
+ value={value}
771
+ visible={showDate}
772
+ />
773
+ )}
774
+ {Boolean(helperText) && <FieldHelperText text={helperText!} />}
775
+ </>
776
+ );
777
+ };