@multiplatform.one/forms 7.36.0 → 7.38.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 (420) hide show
  1. package/dist/cjs/AdaptivePopup/index.native.js +1 -1
  2. package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
  3. package/dist/cjs/Button/index.native.js +1 -1
  4. package/dist/cjs/Button/index.native.js.map +1 -1
  5. package/dist/cjs/Chip/index.native.js.map +1 -1
  6. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js +1 -1
  7. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  8. package/dist/cjs/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
  9. package/dist/cjs/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
  10. package/dist/cjs/ContextualSaveBar/index.native.js +3 -3
  11. package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
  12. package/dist/cjs/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
  13. package/dist/cjs/ControlGroup/index.native.js +1 -1
  14. package/dist/cjs/ControlGroup/index.native.js.map +1 -1
  15. package/dist/cjs/DescriptionList/index.native.js +2 -2
  16. package/dist/cjs/DescriptionList/index.native.js.map +1 -1
  17. package/dist/cjs/ErrorSummary.native.js.map +1 -1
  18. package/dist/cjs/FieldDisplay/editMode.native.js.map +1 -1
  19. package/dist/cjs/FieldDisplay/formatters.native.js.map +1 -1
  20. package/dist/cjs/FieldDisplay/index.native.js +1 -1
  21. package/dist/cjs/FieldDisplay/index.native.js.map +1 -1
  22. package/dist/cjs/FieldGroup.native.js.map +1 -1
  23. package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -1
  24. package/dist/cjs/FloatingPanel/pointAnchor.native.js.map +1 -1
  25. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  26. package/dist/cjs/Form/index.native.js +1 -1
  27. package/dist/cjs/Form/index.native.js.map +1 -1
  28. package/dist/cjs/Form/submissionController.native.js.map +1 -1
  29. package/dist/cjs/Form/validateOn.native.js.map +1 -1
  30. package/dist/cjs/FormGrid.native.js.map +1 -1
  31. package/dist/cjs/FormSection.native.js.map +1 -1
  32. package/dist/cjs/InputParts/index.native.js.map +1 -1
  33. package/dist/cjs/Label/index.native.js.map +1 -1
  34. package/dist/cjs/MediaPicker/base64.native.js.map +1 -1
  35. package/dist/cjs/MediaPicker/payload.native.js.map +1 -1
  36. package/dist/cjs/MediaPicker/types.native.js.map +1 -1
  37. package/dist/cjs/MediaPicker/useMediaPicker.native.js.map +1 -1
  38. package/dist/cjs/Skeleton/index.native.js.map +1 -1
  39. package/dist/cjs/Spinner/SpinnerGlyph.native.js.map +1 -1
  40. package/dist/cjs/Spinner/spinnerSize.native.js.map +1 -1
  41. package/dist/cjs/Table/index.native.js.map +1 -1
  42. package/dist/cjs/Wheel/{WheelEdgeFade.cjs → NativeWheel.cjs} +5 -5
  43. package/dist/cjs/Wheel/NativeWheel.native.js +408 -0
  44. package/dist/cjs/Wheel/NativeWheel.native.js.map +1 -0
  45. package/dist/cjs/Wheel/haptic.cjs +3 -4
  46. package/dist/cjs/Wheel/haptic.native.js +4 -5
  47. package/dist/cjs/Wheel/haptic.native.js.map +1 -1
  48. package/dist/cjs/Wheel/index.cjs +62 -218
  49. package/dist/cjs/Wheel/index.native.js +54 -228
  50. package/dist/cjs/Wheel/index.native.js.map +1 -1
  51. package/dist/cjs/Wheel/wheelShared.cjs +156 -0
  52. package/dist/cjs/Wheel/wheelShared.native.js +169 -0
  53. package/dist/cjs/Wheel/wheelShared.native.js.map +1 -0
  54. package/dist/cjs/actionLabel.native.js.map +1 -1
  55. package/dist/cjs/fieldDefaults.native.js.map +1 -1
  56. package/dist/cjs/fieldLayout.native.js +1 -1
  57. package/dist/cjs/fieldLayout.native.js.map +1 -1
  58. package/dist/cjs/fields/Barcode/BarcodePreview.native.js.map +1 -1
  59. package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +1 -1
  60. package/dist/cjs/fields/Barcode/code128.native.js.map +1 -1
  61. package/dist/cjs/fields/Barcode/index.native.js +2 -2
  62. package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
  63. package/dist/cjs/fields/Barcode/qr.native.js.map +1 -1
  64. package/dist/cjs/fields/Barcode/scannerTypes.native.js.map +1 -1
  65. package/dist/cjs/fields/Checkbox/Checkbox.native.js +1 -1
  66. package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
  67. package/dist/cjs/fields/Checkbox/CheckboxBox.native.js.map +1 -1
  68. package/dist/cjs/fields/Checkbox/CheckboxCard.native.js.map +1 -1
  69. package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  70. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +1 -1
  71. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  72. package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
  73. package/dist/cjs/fields/Checkbox/index.native.js.map +1 -1
  74. package/dist/cjs/fields/ColorPicker/index.native.js +3 -3
  75. package/dist/cjs/fields/ColorPicker/index.native.js.map +1 -1
  76. package/dist/cjs/fields/ColorPicker/parts.native.js +2 -2
  77. package/dist/cjs/fields/ColorPicker/parts.native.js.map +1 -1
  78. package/dist/cjs/fields/Combobox/authorOptions.native.js.map +1 -1
  79. package/dist/cjs/fields/Combobox/index.native.js +6 -6
  80. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  81. package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
  82. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
  83. package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  84. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +3 -3
  85. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  86. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +2 -2
  87. package/dist/cjs/fields/DatePicker/MonthPicker.native.js +2 -2
  88. package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
  89. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
  90. package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  91. package/dist/cjs/fields/DatePicker/catalog.native.js +2 -2
  92. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -1
  93. package/dist/cjs/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  94. package/dist/cjs/fields/DatePicker/index.native.js +2 -2
  95. package/dist/cjs/fields/DatePicker/parts.native.js +2 -2
  96. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  97. package/dist/cjs/fields/DatePicker/utils.native.js.map +1 -1
  98. package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
  99. package/dist/cjs/fields/Duration/index.native.js +3 -3
  100. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  101. package/dist/cjs/fields/FileUpload/filePickerTypes.native.js.map +1 -1
  102. package/dist/cjs/fields/FileUpload/index.native.js +2 -2
  103. package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
  104. package/dist/cjs/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
  105. package/dist/cjs/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
  106. package/dist/cjs/fields/Geolocation/geoValue.native.js.map +1 -1
  107. package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -1
  108. package/dist/cjs/fields/Geolocation/geocoderContext.native.js.map +1 -1
  109. package/dist/cjs/fields/Geolocation/index.native.js +5 -5
  110. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  111. package/dist/cjs/fields/Geolocation/mapMath.native.js.map +1 -1
  112. package/dist/cjs/fields/Geolocation/mapTypes.native.js.map +1 -1
  113. package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
  114. package/dist/cjs/fields/Geolocation/photonGeocoder.native.js.map +1 -1
  115. package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
  116. package/dist/cjs/fields/Icon/index.native.js +3 -3
  117. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  118. package/dist/cjs/fields/ImageField/index.native.js +2 -2
  119. package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
  120. package/dist/cjs/fields/Input/index.native.js +2 -2
  121. package/dist/cjs/fields/Input/index.native.js.map +1 -1
  122. package/dist/cjs/fields/MentionInput/index.native.js +3 -3
  123. package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
  124. package/dist/cjs/fields/OTPInput/index.native.js +3 -3
  125. package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
  126. package/dist/cjs/fields/OTPInput/otpCellCss.native.js.map +1 -1
  127. package/dist/cjs/fields/PhoneInput/dialCodes.native.js.map +1 -1
  128. package/dist/cjs/fields/PhoneInput/index.native.js +3 -3
  129. package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
  130. package/dist/cjs/fields/PhoneInput/phone.native.js.map +1 -1
  131. package/dist/cjs/fields/PhoneInput/regionNames.native.js.map +1 -1
  132. package/dist/cjs/fields/Progress/index.native.js +1 -1
  133. package/dist/cjs/fields/Progress/index.native.js.map +1 -1
  134. package/dist/cjs/fields/RadioGroup/index.native.js +1 -1
  135. package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
  136. package/dist/cjs/fields/Rating/index.native.js +1 -1
  137. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  138. package/dist/cjs/fields/Rating/ratingMath.native.js.map +1 -1
  139. package/dist/cjs/fields/Rating/types.native.js.map +1 -1
  140. package/dist/cjs/fields/SearchInput/index.native.js +2 -2
  141. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  142. package/dist/cjs/fields/Select/index.native.js +4 -4
  143. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  144. package/dist/cjs/fields/Signature/index.native.js +2 -2
  145. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  146. package/dist/cjs/fields/Slider/index.native.js +1 -1
  147. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  148. package/dist/cjs/fields/Slider/jumpMotion.native.js.map +1 -1
  149. package/dist/cjs/fields/Stepper/index.native.js +2 -2
  150. package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
  151. package/dist/cjs/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
  152. package/dist/cjs/fields/Switch/footprint.native.js.map +1 -1
  153. package/dist/cjs/fields/Switch/index.native.js +2 -2
  154. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  155. package/dist/cjs/fields/TextArea/index.native.js +2 -2
  156. package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
  157. package/dist/cjs/fields/TimePicker/catalog.native.js +3 -3
  158. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -1
  159. package/dist/cjs/fields/TimePicker/index.native.js +2 -2
  160. package/dist/cjs/fields/TimePicker/parts.native.js +1 -1
  161. package/dist/cjs/fields/TimePicker/parts.native.js.map +1 -1
  162. package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  163. package/dist/cjs/fields/TimeRangeScrubber/index.native.js +2 -2
  164. package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
  165. package/dist/cjs/fields/ToggleGroup/index.native.js +1 -1
  166. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  167. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  168. package/dist/cjs/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
  169. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  170. package/dist/cjs/fields/nativeSelect.native.js.map +1 -1
  171. package/dist/cjs/fields/selectedOrder.native.js.map +1 -1
  172. package/dist/cjs/formSpacing.native.js.map +1 -1
  173. package/dist/cjs/hooks/useFocusManagement.native.js.map +1 -1
  174. package/dist/cjs/index.native.js +43 -43
  175. package/dist/cjs/index.native.js.map +1 -1
  176. package/dist/cjs/requiredMarking.native.js.map +1 -1
  177. package/dist/cjs/shared/TimeColumn.cjs +0 -9
  178. package/dist/cjs/shared/TimeColumn.native.js +1 -10
  179. package/dist/cjs/shared/TimeColumn.native.js.map +1 -1
  180. package/dist/cjs/shared/capabilityPreflight.native.js.map +1 -1
  181. package/dist/cjs/shared/colorRamps.native.js.map +1 -1
  182. package/dist/cjs/shared/devWarn.native.js.map +1 -1
  183. package/dist/cjs/shared/floatingList.native.js +1 -1
  184. package/dist/cjs/shared/floatingList.native.js.map +1 -1
  185. package/dist/cjs/shared/focusManagement.native.js.map +1 -1
  186. package/dist/cjs/shared/groupContext.native.js.map +1 -1
  187. package/dist/cjs/shared/i18n.native.js.map +1 -1
  188. package/dist/cjs/shared/instantMotion.native.js.map +1 -1
  189. package/dist/cjs/shared/nativeA11y.native.js.map +1 -1
  190. package/dist/cjs/shared/panelPortal.native.js.map +1 -1
  191. package/dist/cjs/shared/pickerTriggerContract.native.js.map +1 -1
  192. package/dist/cjs/shared/pressSlopProps.native.js.map +1 -1
  193. package/dist/cjs/shared/t.native.js.map +1 -1
  194. package/dist/cjs/shared/tableCellContext.native.js.map +1 -1
  195. package/dist/cjs/shared/useDragScroll.native.js.map +1 -1
  196. package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
  197. package/dist/cjs/shared/useSheetEscapeDismiss.native.js.map +1 -1
  198. package/dist/cjs/shared/useWheelIncrement.native.js.map +1 -1
  199. package/dist/cjs/shared/utils.native.js +1 -1
  200. package/dist/cjs/shared/utils.native.js.map +1 -1
  201. package/dist/cjs/shared/zIndex.native.js.map +1 -1
  202. package/dist/cjs/themedPrimitives.native.js.map +1 -1
  203. package/dist/cjs/types.native.js.map +1 -1
  204. package/dist/cjs/utils/LinkCell.native.js.map +1 -1
  205. package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
  206. package/dist/esm/Button/index.native.js.map +1 -1
  207. package/dist/esm/Chip/index.native.js.map +1 -1
  208. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  209. package/dist/esm/ContextualSaveBar/clientKnownIdentity.native.js.map +1 -1
  210. package/dist/esm/ContextualSaveBar/commitImmediateField.native.js.map +1 -1
  211. package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
  212. package/dist/esm/ContextualSaveBar/useUnsavedChangesGuard.native.js.map +1 -1
  213. package/dist/esm/ControlGroup/index.native.js.map +1 -1
  214. package/dist/esm/DescriptionList/index.native.js.map +1 -1
  215. package/dist/esm/ErrorSummary.native.js.map +1 -1
  216. package/dist/esm/FieldDisplay/editMode.native.js.map +1 -1
  217. package/dist/esm/FieldDisplay/formatters.native.js.map +1 -1
  218. package/dist/esm/FieldDisplay/index.native.js.map +1 -1
  219. package/dist/esm/FieldGroup.native.js.map +1 -1
  220. package/dist/esm/FloatingPanel/nestedAction.native.js.map +1 -1
  221. package/dist/esm/FloatingPanel/pointAnchor.native.js.map +1 -1
  222. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  223. package/dist/esm/Form/index.native.js.map +1 -1
  224. package/dist/esm/Form/submissionController.native.js.map +1 -1
  225. package/dist/esm/Form/validateOn.native.js.map +1 -1
  226. package/dist/esm/FormGrid.native.js.map +1 -1
  227. package/dist/esm/FormSection.native.js.map +1 -1
  228. package/dist/esm/InputParts/index.native.js.map +1 -1
  229. package/dist/esm/Label/index.native.js.map +1 -1
  230. package/dist/esm/MediaPicker/base64.native.js.map +1 -1
  231. package/dist/esm/MediaPicker/payload.native.js.map +1 -1
  232. package/dist/esm/MediaPicker/types.native.js.map +1 -1
  233. package/dist/esm/MediaPicker/useMediaPicker.native.js.map +1 -1
  234. package/dist/esm/Skeleton/index.native.js.map +1 -1
  235. package/dist/esm/Spinner/SpinnerGlyph.native.js.map +1 -1
  236. package/dist/esm/Spinner/spinnerSize.native.js.map +1 -1
  237. package/dist/esm/Table/index.native.js.map +1 -1
  238. package/dist/esm/Wheel/NativeWheel.mjs +5 -0
  239. package/dist/esm/Wheel/NativeWheel.mjs.map +1 -0
  240. package/dist/esm/Wheel/NativeWheel.native.js +380 -0
  241. package/dist/esm/Wheel/NativeWheel.native.js.map +1 -0
  242. package/dist/esm/Wheel/haptic.mjs +3 -4
  243. package/dist/esm/Wheel/haptic.mjs.map +1 -1
  244. package/dist/esm/Wheel/haptic.native.js +4 -5
  245. package/dist/esm/Wheel/haptic.native.js.map +1 -1
  246. package/dist/esm/Wheel/index.mjs +49 -205
  247. package/dist/esm/Wheel/index.mjs.map +1 -1
  248. package/dist/esm/Wheel/index.native.js +41 -215
  249. package/dist/esm/Wheel/index.native.js.map +1 -1
  250. package/dist/esm/Wheel/wheelShared.mjs +119 -0
  251. package/dist/esm/Wheel/wheelShared.mjs.map +1 -0
  252. package/dist/esm/Wheel/wheelShared.native.js +129 -0
  253. package/dist/esm/Wheel/wheelShared.native.js.map +1 -0
  254. package/dist/esm/actionLabel.native.js.map +1 -1
  255. package/dist/esm/fieldDefaults.native.js.map +1 -1
  256. package/dist/esm/fieldLayout.native.js.map +1 -1
  257. package/dist/esm/fields/Barcode/BarcodePreview.native.js.map +1 -1
  258. package/dist/esm/fields/Barcode/code128.native.js.map +1 -1
  259. package/dist/esm/fields/Barcode/index.native.js.map +1 -1
  260. package/dist/esm/fields/Barcode/qr.native.js.map +1 -1
  261. package/dist/esm/fields/Barcode/scannerTypes.native.js.map +1 -1
  262. package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
  263. package/dist/esm/fields/Checkbox/CheckboxBox.native.js.map +1 -1
  264. package/dist/esm/fields/Checkbox/CheckboxCard.native.js.map +1 -1
  265. package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
  266. package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  267. package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -1
  268. package/dist/esm/fields/Checkbox/index.native.js.map +1 -1
  269. package/dist/esm/fields/ColorPicker/index.native.js.map +1 -1
  270. package/dist/esm/fields/ColorPicker/parts.native.js.map +1 -1
  271. package/dist/esm/fields/Combobox/authorOptions.native.js.map +1 -1
  272. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  273. package/dist/esm/fields/DatePicker/Calendar.native.js.map +1 -1
  274. package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
  275. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
  276. package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
  277. package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
  278. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -1
  279. package/dist/esm/fields/DatePicker/fieldValueSync.native.js.map +1 -1
  280. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  281. package/dist/esm/fields/DatePicker/utils.native.js.map +1 -1
  282. package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
  283. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  284. package/dist/esm/fields/FileUpload/filePickerTypes.native.js.map +1 -1
  285. package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
  286. package/dist/esm/fields/FileUpload/nativePickerLoader.native.js.map +1 -1
  287. package/dist/esm/fields/Geolocation/expoMapsLoader.native.js.map +1 -1
  288. package/dist/esm/fields/Geolocation/geoValue.native.js.map +1 -1
  289. package/dist/esm/fields/Geolocation/geocoder.native.js.map +1 -1
  290. package/dist/esm/fields/Geolocation/geocoderContext.native.js.map +1 -1
  291. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  292. package/dist/esm/fields/Geolocation/mapMath.native.js.map +1 -1
  293. package/dist/esm/fields/Geolocation/mapTypes.native.js.map +1 -1
  294. package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js.map +1 -1
  295. package/dist/esm/fields/Geolocation/photonGeocoder.native.js.map +1 -1
  296. package/dist/esm/fields/Icon/drawnSet.native.js.map +1 -1
  297. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  298. package/dist/esm/fields/ImageField/index.native.js.map +1 -1
  299. package/dist/esm/fields/Input/index.native.js.map +1 -1
  300. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  301. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  302. package/dist/esm/fields/OTPInput/otpCellCss.native.js.map +1 -1
  303. package/dist/esm/fields/PhoneInput/dialCodes.native.js.map +1 -1
  304. package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
  305. package/dist/esm/fields/PhoneInput/phone.native.js.map +1 -1
  306. package/dist/esm/fields/PhoneInput/regionNames.native.js.map +1 -1
  307. package/dist/esm/fields/Progress/index.native.js.map +1 -1
  308. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  309. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  310. package/dist/esm/fields/Rating/ratingMath.native.js.map +1 -1
  311. package/dist/esm/fields/Rating/types.native.js.map +1 -1
  312. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  313. package/dist/esm/fields/Select/index.native.js.map +1 -1
  314. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  315. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  316. package/dist/esm/fields/Slider/jumpMotion.native.js.map +1 -1
  317. package/dist/esm/fields/Stepper/index.native.js.map +1 -1
  318. package/dist/esm/fields/Stepper/resolveStepperPlacement.native.js.map +1 -1
  319. package/dist/esm/fields/Switch/footprint.native.js.map +1 -1
  320. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  321. package/dist/esm/fields/TextArea/index.native.js.map +1 -1
  322. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -1
  323. package/dist/esm/fields/TimePicker/parts.native.js.map +1 -1
  324. package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  325. package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
  326. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  327. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  328. package/dist/esm/fields/nativeDateTime/dateTimePickerLoader.native.js.map +1 -1
  329. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  330. package/dist/esm/fields/nativeSelect.native.js.map +1 -1
  331. package/dist/esm/fields/selectedOrder.native.js.map +1 -1
  332. package/dist/esm/formSpacing.native.js.map +1 -1
  333. package/dist/esm/hooks/useFocusManagement.native.js.map +1 -1
  334. package/dist/esm/index.native.js.map +1 -1
  335. package/dist/esm/requiredMarking.native.js.map +1 -1
  336. package/dist/esm/shared/TimeColumn.mjs +0 -9
  337. package/dist/esm/shared/TimeColumn.mjs.map +1 -1
  338. package/dist/esm/shared/TimeColumn.native.js +0 -9
  339. package/dist/esm/shared/TimeColumn.native.js.map +1 -1
  340. package/dist/esm/shared/capabilityPreflight.native.js.map +1 -1
  341. package/dist/esm/shared/colorRamps.native.js.map +1 -1
  342. package/dist/esm/shared/devWarn.native.js.map +1 -1
  343. package/dist/esm/shared/floatingList.native.js.map +1 -1
  344. package/dist/esm/shared/focusManagement.native.js.map +1 -1
  345. package/dist/esm/shared/groupContext.native.js.map +1 -1
  346. package/dist/esm/shared/i18n.native.js.map +1 -1
  347. package/dist/esm/shared/instantMotion.native.js.map +1 -1
  348. package/dist/esm/shared/nativeA11y.native.js.map +1 -1
  349. package/dist/esm/shared/panelPortal.native.js.map +1 -1
  350. package/dist/esm/shared/pickerTriggerContract.native.js.map +1 -1
  351. package/dist/esm/shared/pressSlopProps.native.js.map +1 -1
  352. package/dist/esm/shared/t.native.js.map +1 -1
  353. package/dist/esm/shared/tableCellContext.native.js.map +1 -1
  354. package/dist/esm/shared/useDragScroll.native.js.map +1 -1
  355. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  356. package/dist/esm/shared/useSheetEscapeDismiss.native.js.map +1 -1
  357. package/dist/esm/shared/useWheelIncrement.native.js.map +1 -1
  358. package/dist/esm/shared/utils.native.js.map +1 -1
  359. package/dist/esm/shared/zIndex.native.js.map +1 -1
  360. package/dist/esm/themedPrimitives.native.js.map +1 -1
  361. package/dist/esm/types.native.js.map +1 -1
  362. package/dist/esm/utils/LinkCell.native.js.map +1 -1
  363. package/dist/jsx/Wheel/NativeWheel.mjs +5 -0
  364. package/dist/jsx/Wheel/NativeWheel.mjs.map +1 -0
  365. package/dist/jsx/Wheel/NativeWheel.native.js +408 -0
  366. package/dist/jsx/Wheel/NativeWheel.native.js.map +1 -0
  367. package/dist/jsx/Wheel/haptic.mjs +3 -4
  368. package/dist/jsx/Wheel/haptic.mjs.map +1 -1
  369. package/dist/jsx/Wheel/haptic.native.js +4 -5
  370. package/dist/jsx/Wheel/haptic.native.js.map +1 -1
  371. package/dist/jsx/Wheel/index.mjs +49 -205
  372. package/dist/jsx/Wheel/index.mjs.map +1 -1
  373. package/dist/jsx/Wheel/index.native.js +54 -228
  374. package/dist/jsx/Wheel/index.native.js.map +1 -1
  375. package/dist/jsx/Wheel/wheelShared.mjs +119 -0
  376. package/dist/jsx/Wheel/wheelShared.mjs.map +1 -0
  377. package/dist/jsx/Wheel/wheelShared.native.js +169 -0
  378. package/dist/jsx/Wheel/wheelShared.native.js.map +1 -0
  379. package/dist/jsx/index.native.js.map +1 -1
  380. package/dist/jsx/shared/TimeColumn.mjs +0 -9
  381. package/dist/jsx/shared/TimeColumn.mjs.map +1 -1
  382. package/dist/jsx/shared/TimeColumn.native.js +0 -9
  383. package/dist/jsx/shared/TimeColumn.native.js.map +1 -1
  384. package/package.json +8 -8
  385. package/src/Wheel/NativeWheel.native.spec.tsx +181 -0
  386. package/src/Wheel/NativeWheel.native.tsx +430 -0
  387. package/src/Wheel/NativeWheel.tsx +6 -0
  388. package/src/Wheel/haptic.ts +9 -8
  389. package/src/Wheel/index.tsx +121 -363
  390. package/src/Wheel/wheelShared.spec.ts +53 -0
  391. package/src/Wheel/wheelShared.ts +222 -0
  392. package/src/shared/TimeColumn.tsx +0 -7
  393. package/types/Wheel/NativeWheel.d.ts +4 -0
  394. package/types/Wheel/NativeWheel.d.ts.map +1 -0
  395. package/types/Wheel/NativeWheel.native.d.ts +9 -0
  396. package/types/Wheel/NativeWheel.native.d.ts.map +1 -0
  397. package/types/Wheel/haptic.d.ts +4 -2
  398. package/types/Wheel/haptic.d.ts.map +1 -1
  399. package/types/Wheel/index.d.ts +4 -49
  400. package/types/Wheel/index.d.ts.map +1 -1
  401. package/types/Wheel/wheelShared.d.ts +87 -0
  402. package/types/Wheel/wheelShared.d.ts.map +1 -0
  403. package/types/shared/TimeColumn.d.ts.map +1 -1
  404. package/dist/cjs/Wheel/WheelEdgeFade.native.js +0 -116
  405. package/dist/cjs/Wheel/WheelEdgeFade.native.js.map +0 -1
  406. package/dist/esm/Wheel/WheelEdgeFade.mjs +0 -5
  407. package/dist/esm/Wheel/WheelEdgeFade.mjs.map +0 -1
  408. package/dist/esm/Wheel/WheelEdgeFade.native.js +0 -88
  409. package/dist/esm/Wheel/WheelEdgeFade.native.js.map +0 -1
  410. package/dist/jsx/Wheel/WheelEdgeFade.mjs +0 -5
  411. package/dist/jsx/Wheel/WheelEdgeFade.mjs.map +0 -1
  412. package/dist/jsx/Wheel/WheelEdgeFade.native.js +0 -116
  413. package/dist/jsx/Wheel/WheelEdgeFade.native.js.map +0 -1
  414. package/src/Wheel/WheelEdgeFade.native.tsx +0 -67
  415. package/src/Wheel/WheelEdgeFade.spec.tsx +0 -141
  416. package/src/Wheel/WheelEdgeFade.tsx +0 -7
  417. package/types/Wheel/WheelEdgeFade.d.ts +0 -8
  418. package/types/Wheel/WheelEdgeFade.d.ts.map +0 -1
  419. package/types/Wheel/WheelEdgeFade.native.d.ts +0 -8
  420. package/types/Wheel/WheelEdgeFade.native.d.ts.map +0 -1
@@ -0,0 +1,181 @@
1
+ import { act, cleanup } from "@testing-library/react";
2
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
3
+ import type { ReactNode } from "react";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { NativeWheel } from "./NativeWheel.native";
6
+ import { fontLineHeightPx, tokenPx } from "./wheelShared";
7
+
8
+ type ScrollHandler = (...args: unknown[]) => void;
9
+ type ScrollProps = { children?: ReactNode } & Partial<Record<string, ScrollHandler | ReactNode>>;
10
+
11
+ const scroll = vi.hoisted(() => ({
12
+ props: null as ScrollProps | null,
13
+ scrollTo: vi.fn(),
14
+ }));
15
+
16
+ vi.mock("tamagui", async (importOriginal) => ({
17
+ ...(await importOriginal<typeof import("tamagui")>()),
18
+ isWeb: false,
19
+ }));
20
+
21
+ // The vitest config rewrites `from "react-native"` to react-native-web.
22
+ vi.mock("react-native-web", async (importOriginal) => {
23
+ const rn = await importOriginal<typeof import("react-native")>();
24
+ const { forwardRef, useImperativeHandle } = await import("react");
25
+ const ScrollView = forwardRef<unknown, ScrollProps>(function ScrollView(props, ref) {
26
+ scroll.props = props;
27
+ useImperativeHandle(ref, () => ({ scrollTo: scroll.scrollTo }));
28
+ return <div data-testid="scroll">{props.children as ReactNode}</div>;
29
+ });
30
+ return {
31
+ ...rn,
32
+ Animated: {
33
+ ...rn.Animated,
34
+ ScrollView,
35
+ event: (_mapping: unknown, config: { listener: (e: unknown) => void }) => config.listener,
36
+ },
37
+ };
38
+ });
39
+
40
+ afterEach(() => {
41
+ cleanup();
42
+ scroll.props = null;
43
+ scroll.scrollTo.mockReset();
44
+ });
45
+
46
+ const hours = Array.from({ length: 24 }, (_, h) => ({
47
+ value: h,
48
+ label: String(h).padStart(2, "0"),
49
+ }));
50
+ const fire = (name: string, arg?: unknown) =>
51
+ (scroll.props?.[name] as ScrollHandler | undefined)?.(arg);
52
+ const at = (y: number) => ({ nativeEvent: { contentOffset: { x: 0, y } } });
53
+ const rowHeight = () => (scroll.props as unknown as { snapToInterval: number }).snapToInterval;
54
+
55
+ describe("native Wheel on a UIScrollView", () => {
56
+ it("snaps to whole rows with the platform's own momentum", () => {
57
+ renderWithProviders(<NativeWheel items={hours} value={9} />);
58
+ const props = scroll.props as unknown as Record<string, unknown>;
59
+ expect(props.snapToInterval).toBeGreaterThan(0);
60
+ expect(props.decelerationRate).toBe("normal");
61
+ expect(props.contentOffset).toEqual({ x: 0, y: 9 * rowHeight() });
62
+ });
63
+
64
+ it("sizes rows from the font's line height plus the $1 space token", () => {
65
+ renderWithProviders(<NativeWheel items={hours} value={9} size="$8" />);
66
+ expect(rowHeight()).toBe(Math.ceil(fontLineHeightPx("$8", "$body") + tokenPx("$1", "space")));
67
+ expect(rowHeight()).toBeGreaterThan(fontLineHeightPx("$8", "$body"));
68
+ });
69
+
70
+ it("reports exactly the row centred when a fling comes to rest", () => {
71
+ const onChange = vi.fn();
72
+ renderWithProviders(<NativeWheel items={hours} value={9} onChange={onChange} />);
73
+ const h = rowHeight();
74
+ act(() => {
75
+ fire("onScrollBeginDrag");
76
+ fire("onScroll", at(11.4 * h));
77
+ fire("onScrollEndDrag");
78
+ fire("onMomentumScrollBegin");
79
+ fire("onScroll", at(14 * h));
80
+ fire("onMomentumScrollEnd", at(14 * h));
81
+ });
82
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(14);
83
+ });
84
+
85
+ it("seats a row the snap left between two rows before reporting it", () => {
86
+ const onChange = vi.fn();
87
+ renderWithProviders(<NativeWheel items={hours} value={9} onChange={onChange} />);
88
+ const h = rowHeight();
89
+ act(() => {
90
+ fire("onMomentumScrollEnd", at(5.6 * h));
91
+ });
92
+ expect(scroll.scrollTo).toHaveBeenCalledWith({ y: 6 * h, animated: expect.any(Boolean) });
93
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(6);
94
+ });
95
+
96
+ it("fires onItemCross for each row that crosses the band", () => {
97
+ const onItemCross = vi.fn();
98
+ renderWithProviders(<NativeWheel items={hours} value={0} onItemCross={onItemCross} />);
99
+ const h = rowHeight();
100
+ act(() => {
101
+ fire("onScroll", at(0.6 * h));
102
+ fire("onScroll", at(1.2 * h));
103
+ fire("onScroll", at(1.6 * h));
104
+ });
105
+ expect(onItemCross.mock.calls).toEqual([
106
+ [1, 1],
107
+ [2, 2],
108
+ ]);
109
+ });
110
+
111
+ it("loops: a looping column lays out copies and reports the wrapped value", () => {
112
+ const onChange = vi.fn();
113
+ renderWithProviders(<NativeWheel items={hours} value={23} loop onChange={onChange} />);
114
+ const h = rowHeight();
115
+ const start = (scroll.props as unknown as { contentOffset: { y: number } }).contentOffset.y;
116
+ act(() => {
117
+ fire("onMomentumScrollEnd", at(start + h));
118
+ });
119
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(0);
120
+ });
121
+
122
+ it("is one adjustable element named for the column", () => {
123
+ const { getByTestId } = renderWithProviders(
124
+ <NativeWheel items={hours} value={9} aria-label="Hour" />,
125
+ );
126
+ const wheel = getByTestId("wheel");
127
+ expect(wheel.getAttribute("role")).toBe("slider");
128
+ expect(wheel.getAttribute("aria-label")).toBe("Hour");
129
+ });
130
+
131
+ it("taps a row on the drum to bring it to the band", () => {
132
+ const onChange = vi.fn();
133
+ renderWithProviders(<NativeWheel items={hours} value={0} onChange={onChange} />);
134
+ const h = rowHeight();
135
+ const frame = h * 5;
136
+ const radius = frame / 2 / Math.sin((80 * Math.PI) / 180);
137
+ const twoBelow = frame / 2 + radius * Math.sin((2 * h) / radius);
138
+ act(() => {
139
+ fire("onTouchStart", { nativeEvent: { pageY: 500, locationY: twoBelow } });
140
+ fire("onTouchEnd", { nativeEvent: { pageY: 501, locationY: twoBelow } });
141
+ });
142
+ expect(scroll.scrollTo).toHaveBeenCalledWith({ y: 2 * h, animated: expect.any(Boolean) });
143
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(2);
144
+ });
145
+
146
+ it("seats and reports the row when a touch stops a fling (no momentum end on iOS)", () => {
147
+ vi.useFakeTimers();
148
+ try {
149
+ const onChange = vi.fn();
150
+ renderWithProviders(<NativeWheel items={hours} value={9} onChange={onChange} />);
151
+ const h = rowHeight();
152
+ act(() => {
153
+ fire("onScrollBeginDrag");
154
+ fire("onScrollEndDrag");
155
+ fire("onMomentumScrollBegin");
156
+ fire("onScroll", at(15.3 * h));
157
+ fire("onTouchStart", { nativeEvent: { pageY: 400, locationY: 0 } });
158
+ fire("onTouchEnd", { nativeEvent: { pageY: 400, locationY: 0 } });
159
+ vi.advanceTimersByTime(500);
160
+ });
161
+ expect(scroll.scrollTo).toHaveBeenCalledWith({ y: 15 * h, animated: expect.any(Boolean) });
162
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(15);
163
+ } finally {
164
+ vi.useRealTimers();
165
+ }
166
+ });
167
+
168
+ it("reseats a value-less column when its items shrink under it", () => {
169
+ const onChange = vi.fn();
170
+ const { rerender } = renderWithProviders(
171
+ <NativeWheel items={hours} value={20} onChange={onChange} />,
172
+ );
173
+ rerender(<NativeWheel items={hours.slice(0, 10)} value={20} onChange={onChange} />);
174
+ expect(onChange).toHaveBeenCalledExactlyOnceWith(9);
175
+ });
176
+
177
+ it("does not scroll while disabled", () => {
178
+ renderWithProviders(<NativeWheel items={hours} value={9} disabled />);
179
+ expect((scroll.props as unknown as { scrollEnabled: boolean }).scrollEnabled).toBe(false);
180
+ });
181
+ });
@@ -0,0 +1,430 @@
1
+ import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
+ import type { ReactNode } from "react";
3
+ import {
4
+ Animated,
5
+ type AccessibilityActionEvent,
6
+ type GestureResponderEvent,
7
+ type NativeScrollEvent,
8
+ type NativeSyntheticEvent,
9
+ View as RNView,
10
+ } from "react-native";
11
+ import { Text, View } from "tamagui";
12
+ import { t } from "../shared/t";
13
+ import { fireSelectionHaptic } from "./haptic";
14
+ import {
15
+ defaultColumnWidth,
16
+ drumPose,
17
+ drumRadius,
18
+ drumReachRows,
19
+ getItemLabel,
20
+ getItemValue,
21
+ modIndex,
22
+ nearestRowForDataIndex,
23
+ useWheelLayout,
24
+ type WheelProps,
25
+ } from "./wheelShared";
26
+
27
+ /** Rows a looping wheel lays out, so a fling never reaches the ends before it recentres. */
28
+ const loopRowBudget = 1200;
29
+ const tapSlopPx = 8;
30
+ const tapMaxMs = 300;
31
+ const settleFallbackMs = 120;
32
+ const poseStep = 0.25;
33
+
34
+ interface DrumRowProps {
35
+ row: number;
36
+ scrollY: Animated.Value;
37
+ itemHeight: number;
38
+ frameHeight: number;
39
+ padTop: number;
40
+ reach: number;
41
+ children: ReactNode;
42
+ }
43
+
44
+ /**
45
+ * One row on the drum. Its pose is a native-driver interpolation of the
46
+ * scroll offset, so rotateX, scale and opacity track the finger on the UI
47
+ * thread with no JS frame in between.
48
+ */
49
+ const DrumRow = memo(function DrumRow({
50
+ row,
51
+ scrollY,
52
+ itemHeight,
53
+ frameHeight,
54
+ padTop,
55
+ reach,
56
+ children,
57
+ }: DrumRowProps) {
58
+ const style = useMemo(() => {
59
+ const span = 2 * (Math.ceil(reach) + 1);
60
+ const input: number[] = [];
61
+ const translate: number[] = [];
62
+ const rotate: string[] = [];
63
+ const scale: number[] = [];
64
+ const opacity: number[] = [];
65
+ for (let d = span; d >= -span - 1e-9; d -= poseStep) {
66
+ const pose = drumPose(d, itemHeight, frameHeight);
67
+ input.push((row - d) * itemHeight);
68
+ translate.push(pose.offset - d * itemHeight);
69
+ rotate.push(`${pose.rotateX}deg`);
70
+ scale.push(pose.scale);
71
+ opacity.push(pose.opacity);
72
+ }
73
+ const range = (output: number[] | string[]) =>
74
+ scrollY.interpolate({ inputRange: input, outputRange: output, extrapolate: "clamp" });
75
+ return {
76
+ position: "absolute" as const,
77
+ left: 0,
78
+ right: 0,
79
+ top: padTop + row * itemHeight,
80
+ height: itemHeight,
81
+ alignItems: "center" as const,
82
+ justifyContent: "center" as const,
83
+ opacity: range(opacity),
84
+ transform: [
85
+ { perspective: frameHeight * 4 },
86
+ { translateY: range(translate) },
87
+ { rotateX: range(rotate) },
88
+ { scale: range(scale) },
89
+ ],
90
+ };
91
+ }, [row, scrollY, itemHeight, frameHeight, padTop, reach]);
92
+ return (
93
+ <Animated.View pointerEvents="none" style={style}>
94
+ {children}
95
+ </Animated.View>
96
+ );
97
+ });
98
+
99
+ /**
100
+ * UIPickerView on a real UIScrollView: the platform's own momentum, rubber
101
+ * band and paging (snapToInterval) drive the wheel, and nested inside a
102
+ * screen's ScrollView the inner one owns the drag, so the column never drags
103
+ * its page along with it.
104
+ */
105
+ export function NativeWheel<T = string | number>(props: WheelProps<T>) {
106
+ const {
107
+ items,
108
+ value,
109
+ onChange,
110
+ width: widthProp,
111
+ renderItem,
112
+ disabled = false,
113
+ loop = false,
114
+ flex,
115
+ onItemCross,
116
+ "aria-label": ariaLabel,
117
+ } = props;
118
+ const layout = useWheelLayout(props);
119
+ const { itemHeight, frameHeight, size, knobProps, snapDurationMs } = layout;
120
+ const n = items.length;
121
+ const loopEnabled = Boolean(loop && n > 1);
122
+ const copies = loopEnabled ? Math.max(3, Math.ceil(loopRowBudget / n)) | 1 : 1;
123
+ const totalRows = n * copies;
124
+ const middleBase = loopEnabled ? Math.floor(copies / 2) * n : 0;
125
+ const padTop = (frameHeight - itemHeight) / 2;
126
+ const reach = drumReachRows(itemHeight, frameHeight);
127
+ const columnWidth = widthProp ?? defaultColumnWidth;
128
+
129
+ const indexOfValue = useCallback(
130
+ (v: T | undefined) => (v === undefined ? -1 : items.findIndex((it) => getItemValue(it) === v)),
131
+ [items],
132
+ );
133
+
134
+ const initialIndex = Math.max(0, indexOfValue(value));
135
+ const initialRow = middleBase + initialIndex;
136
+ const scrollY = useRef(new Animated.Value(initialRow * itemHeight)).current;
137
+ // iOS shifts the live offset by any change to the contentOffset prop, which
138
+ // would add to an in-flight scrollTo, so the prop is fixed at mount.
139
+ const [initialOffset] = useState(() => ({ x: 0, y: initialRow * itemHeight }));
140
+ const scrollRef = useRef<{ scrollTo: (o: { y: number; animated?: boolean }) => void } | null>(
141
+ null,
142
+ );
143
+ const [centerRow, setCenterRow] = useState(initialRow);
144
+ const centerRowRef = useRef(initialRow);
145
+ const yRef = useRef(initialRow * itemHeight);
146
+ const committedIndexRef = useRef(initialIndex);
147
+ const [committedIndex, setCommittedIndex] = useState(initialIndex);
148
+ const programmaticRef = useRef(false);
149
+ const draggingRef = useRef(false);
150
+ const momentumRef = useRef(false);
151
+ const settleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
152
+ const touchRef = useRef<{ pageY: number; time: number; stoppedMotion: boolean } | null>(null);
153
+ const dragBeganRef = useRef(false);
154
+ const onChangeRef = useRef(onChange);
155
+ onChangeRef.current = onChange;
156
+ const onItemCrossRef = useRef(onItemCross);
157
+ onItemCrossRef.current = onItemCross;
158
+
159
+ const scrollToRow = useCallback(
160
+ (row: number, animated: boolean) => {
161
+ const y = row * itemHeight;
162
+ programmaticRef.current = animated && Math.abs(y - yRef.current) > 0.5;
163
+ if (!animated) {
164
+ yRef.current = y;
165
+ scrollY.setValue(y);
166
+ centerRowRef.current = row;
167
+ setCenterRow(row);
168
+ }
169
+ scrollRef.current?.scrollTo({ y, animated });
170
+ },
171
+ [itemHeight, scrollY],
172
+ );
173
+
174
+ const commit = useCallback(
175
+ (dataIndex: number) => {
176
+ if (n === 0 || dataIndex === committedIndexRef.current) return;
177
+ committedIndexRef.current = dataIndex;
178
+ setCommittedIndex(dataIndex);
179
+ const item = items[dataIndex];
180
+ if (item !== undefined) onChangeRef.current?.(getItemValue(item));
181
+ },
182
+ [items, n],
183
+ );
184
+
185
+ /** Called once motion stops: seat the nearest row exactly, then report it. */
186
+ const settle = useCallback(() => {
187
+ if (n === 0 || draggingRef.current) return;
188
+ programmaticRef.current = false;
189
+ const y = yRef.current;
190
+ const row = Math.max(0, Math.min(totalRows - 1, Math.round(y / itemHeight)));
191
+ const dataIndex = modIndex(row, n);
192
+ if (Math.abs(y - row * itemHeight) > 0.5) {
193
+ scrollToRow(row, snapDurationMs > 0);
194
+ } else if (loopEnabled && Math.abs(row - (middleBase + dataIndex)) >= n) {
195
+ scrollToRow(middleBase + dataIndex, false);
196
+ }
197
+ commit(dataIndex);
198
+ }, [n, totalRows, itemHeight, scrollToRow, snapDurationMs, loopEnabled, middleBase, commit]);
199
+
200
+ const clearSettleTimer = () => {
201
+ if (settleTimerRef.current) clearTimeout(settleTimerRef.current);
202
+ settleTimerRef.current = null;
203
+ };
204
+ useEffect(() => clearSettleTimer, []);
205
+
206
+ const onScroll = useMemo(
207
+ () =>
208
+ Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
209
+ useNativeDriver: true,
210
+ listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
211
+ const y = e.nativeEvent.contentOffset.y;
212
+ yRef.current = y;
213
+ const row = Math.round(y / itemHeight);
214
+ if (row === centerRowRef.current) return;
215
+ centerRowRef.current = row;
216
+ setCenterRow(row);
217
+ if (row < 0 || row >= totalRows || n === 0 || programmaticRef.current) return;
218
+ const dataIndex = modIndex(row, n);
219
+ const item = items[dataIndex];
220
+ if (item === undefined) return;
221
+ onItemCrossRef.current?.(getItemValue(item), dataIndex);
222
+ fireSelectionHaptic();
223
+ },
224
+ }),
225
+ [scrollY, itemHeight, totalRows, n, items],
226
+ );
227
+
228
+ useEffect(() => {
229
+ const index = indexOfValue(value);
230
+ if (index < 0 || draggingRef.current || momentumRef.current) return;
231
+ if (index === committedIndexRef.current && modIndex(centerRowRef.current, n) === index) return;
232
+ committedIndexRef.current = index;
233
+ setCommittedIndex(index);
234
+ const row = loopEnabled ? nearestRowForDataIndex(centerRowRef.current, index, n) : index;
235
+ scrollToRow(row, snapDurationMs > 0);
236
+ }, [value, indexOfValue, loopEnabled, n, scrollToRow, snapDurationMs]);
237
+
238
+ const seatedHeightRef = useRef(itemHeight);
239
+ useLayoutEffect(() => {
240
+ if (seatedHeightRef.current === itemHeight) return;
241
+ seatedHeightRef.current = itemHeight;
242
+ scrollToRow(centerRowRef.current, false);
243
+ }, [itemHeight, scrollToRow]);
244
+
245
+ const seatedCountRef = useRef(n);
246
+ useLayoutEffect(() => {
247
+ if (seatedCountRef.current === n || n === 0) return;
248
+ seatedCountRef.current = n;
249
+ const index = indexOfValue(value);
250
+ const dataIndex = index >= 0 ? index : Math.min(committedIndexRef.current, n - 1);
251
+ scrollToRow(middleBase + dataIndex, false);
252
+ commit(dataIndex);
253
+ }, [n, value, indexOfValue, middleBase, scrollToRow, commit]);
254
+
255
+ const step = useCallback(
256
+ (delta: number) => {
257
+ if (n === 0 || disabled) return;
258
+ const current = committedIndexRef.current;
259
+ const next = loopEnabled
260
+ ? modIndex(current + delta, n)
261
+ : Math.max(0, Math.min(n - 1, current + delta));
262
+ if (next === current) return;
263
+ const row = loopEnabled ? nearestRowForDataIndex(centerRowRef.current, next, n) : next;
264
+ scrollToRow(row, snapDurationMs > 0);
265
+ commit(next);
266
+ },
267
+ [n, disabled, loopEnabled, scrollToRow, snapDurationMs, commit],
268
+ );
269
+
270
+ const onAccessibilityAction = useCallback(
271
+ (e: AccessibilityActionEvent) => {
272
+ if (e.nativeEvent.actionName === "increment") step(1);
273
+ if (e.nativeEvent.actionName === "decrement") step(-1);
274
+ },
275
+ [step],
276
+ );
277
+
278
+ const onTouchStart = useCallback((e: GestureResponderEvent) => {
279
+ // A touch that stops a fling never gets onMomentumScrollEnd on iOS.
280
+ const stoppedMotion = momentumRef.current || programmaticRef.current;
281
+ momentumRef.current = false;
282
+ dragBeganRef.current = false;
283
+ clearSettleTimer();
284
+ touchRef.current = { pageY: e.nativeEvent.pageY, time: Date.now(), stoppedMotion };
285
+ }, []);
286
+
287
+ const onTouchEnd = useCallback(
288
+ (e: GestureResponderEvent) => {
289
+ const touch = touchRef.current;
290
+ touchRef.current = null;
291
+ if (!touch || disabled || n === 0 || dragBeganRef.current) return;
292
+ const isTap =
293
+ Math.abs(e.nativeEvent.pageY - touch.pageY) <= tapSlopPx &&
294
+ Date.now() - touch.time <= tapMaxMs;
295
+ if (touch.stoppedMotion || !isTap) {
296
+ settleTimerRef.current = setTimeout(() => {
297
+ if (!momentumRef.current && !draggingRef.current) settle();
298
+ }, settleFallbackMs);
299
+ return;
300
+ }
301
+ const radius = drumRadius(frameHeight);
302
+ // Rows ignore touches, so locationY is in the content view's space.
303
+ const fromCenter = e.nativeEvent.locationY - yRef.current - frameHeight / 2;
304
+ const theta = Math.asin(Math.max(-1, Math.min(1, fromCenter / radius)));
305
+ const rows = Math.round((theta * radius) / itemHeight);
306
+ if (rows === 0) return;
307
+ const row = Math.max(0, Math.min(totalRows - 1, centerRowRef.current + rows));
308
+ scrollToRow(row, snapDurationMs > 0);
309
+ commit(modIndex(row, n));
310
+ },
311
+ [disabled, n, frameHeight, itemHeight, totalRows, scrollToRow, snapDurationMs, commit, settle],
312
+ );
313
+
314
+ const span = Math.ceil(reach) + 1;
315
+ const rows: number[] = [];
316
+ for (let r = centerRow - span; r <= centerRow + span; r++) {
317
+ if (r >= 0 && r < totalRows) rows.push(r);
318
+ }
319
+
320
+ const committedItem = items[committedIndex];
321
+
322
+ return (
323
+ <RNView
324
+ style={{
325
+ height: frameHeight,
326
+ width: flex ? "100%" : columnWidth,
327
+ minWidth: flex ? 0 : columnWidth,
328
+ flex,
329
+ position: "relative",
330
+ overflow: "hidden",
331
+ }}
332
+ testID="wheel"
333
+ accessible
334
+ accessibilityRole="adjustable"
335
+ accessibilityLabel={ariaLabel ?? t("Value picker")}
336
+ accessibilityState={{ disabled }}
337
+ accessibilityValue={{
338
+ text: committedItem !== undefined ? getItemLabel(committedItem) : undefined,
339
+ }}
340
+ accessibilityActions={disabled ? [] : [{ name: "increment" }, { name: "decrement" }]}
341
+ onAccessibilityAction={onAccessibilityAction}
342
+ >
343
+ <View
344
+ className={layout.highlightClassName}
345
+ position="absolute"
346
+ left={0}
347
+ right={0}
348
+ top={padTop}
349
+ height={itemHeight}
350
+ backgroundColor="$color4"
351
+ pointerEvents="none"
352
+ style={layout.highlightStyle as never}
353
+ data-testid="wheel-selection"
354
+ />
355
+ <Animated.ScrollView
356
+ ref={scrollRef as never}
357
+ testID="wheel-wrapper"
358
+ style={{ flex: 1 }}
359
+ contentOffset={initialOffset}
360
+ showsVerticalScrollIndicator={false}
361
+ scrollEnabled={!disabled && n > 0}
362
+ nestedScrollEnabled
363
+ snapToInterval={itemHeight}
364
+ decelerationRate="normal"
365
+ scrollEventThrottle={16}
366
+ onScroll={onScroll}
367
+ onTouchStart={onTouchStart}
368
+ onTouchEnd={onTouchEnd}
369
+ onScrollBeginDrag={() => {
370
+ clearSettleTimer();
371
+ draggingRef.current = true;
372
+ dragBeganRef.current = true;
373
+ programmaticRef.current = false;
374
+ }}
375
+ onScrollEndDrag={() => {
376
+ draggingRef.current = false;
377
+ clearSettleTimer();
378
+ settleTimerRef.current = setTimeout(() => {
379
+ if (!momentumRef.current) settle();
380
+ }, settleFallbackMs);
381
+ }}
382
+ onMomentumScrollBegin={() => {
383
+ clearSettleTimer();
384
+ momentumRef.current = true;
385
+ }}
386
+ onMomentumScrollEnd={(e: NativeSyntheticEvent<NativeScrollEvent>) => {
387
+ momentumRef.current = false;
388
+ yRef.current = e.nativeEvent.contentOffset.y;
389
+ settle();
390
+ }}
391
+ >
392
+ <RNView style={{ height: totalRows * itemHeight + padTop * 2 }}>
393
+ {rows.map((r) => {
394
+ const dataIndex = modIndex(r, n);
395
+ const item = items[dataIndex];
396
+ const isSelected = r === centerRow;
397
+ return (
398
+ <DrumRow
399
+ key={r}
400
+ row={r}
401
+ scrollY={scrollY}
402
+ itemHeight={itemHeight}
403
+ frameHeight={frameHeight}
404
+ padTop={padTop}
405
+ reach={reach}
406
+ >
407
+ {renderItem ? (
408
+ renderItem(getItemValue(item), dataIndex, isSelected)
409
+ ) : (
410
+ <Text
411
+ {...knobProps.body}
412
+ fontSize={size}
413
+ color={disabled ? "$color10" : isSelected ? "$color12" : "$color11"}
414
+ numberOfLines={1}
415
+ paddingHorizontal="$2"
416
+ data-testid="wheel-item"
417
+ data-index={dataIndex}
418
+ data-selected={isSelected ? true : undefined}
419
+ >
420
+ {getItemLabel(item)}
421
+ </Text>
422
+ )}
423
+ </DrumRow>
424
+ );
425
+ })}
426
+ </RNView>
427
+ </Animated.ScrollView>
428
+ </RNView>
429
+ );
430
+ }
@@ -0,0 +1,6 @@
1
+ import type { WheelProps } from "./wheelShared";
2
+
3
+ /** Web paints the wheel itself (index.tsx); the scroll-view drum is native only. */
4
+ export function NativeWheel<T = string | number>(_props: WheelProps<T>) {
5
+ return null;
6
+ }
@@ -1,15 +1,16 @@
1
1
  import { isWeb } from "tamagui";
2
2
 
3
+ type HapticsModule = { selectionAsync?: () => Promise<void> };
4
+
3
5
  /**
4
- * iOS UIPickerView uses UISelectionFeedbackGenerator, not impact. Optional
5
- * so web / tests / apps without expo-haptics stay silent.
6
+ * iOS UIPickerView ticks with UISelectionFeedbackGenerator. The native module
7
+ * is looked up in the Expo registry instead of importing expo-haptics, so a
8
+ * binary built without it (the Kitchen Sink) stays silent and the wheel still
9
+ * ships over the air.
6
10
  */
7
11
  export function fireSelectionHaptic(): void {
8
12
  if (isWeb) return;
9
- try {
10
- const loaded = require("expo-haptics") as { selectionAsync?: () => Promise<void> };
11
- void loaded.selectionAsync?.();
12
- } catch {
13
- // optional peer
14
- }
13
+ const registry = (globalThis as { expo?: { modules?: Record<string, unknown> } }).expo?.modules;
14
+ const haptics = registry?.ExpoHaptics as HapticsModule | undefined;
15
+ void haptics?.selectionAsync?.()?.catch(() => {});
15
16
  }