@multiplatform.one/forms 7.36.0 → 7.37.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
@@ -1,63 +1,23 @@
1
- import { motionDurationMs } from "@multiplatform.one/theme";
2
- import type { CSSProperties, ReactNode } from "react";
1
+ import type { CSSProperties } from "react";
3
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
- import { PanResponder, type GestureResponderEvent } from "react-native";
5
- import { sizeTokenStep, useResolvedKnobs } from "@multiplatform.one/theme";
6
- import type { GetProps, SizeTokens } from "tamagui";
7
- import { View, getTokens, isWeb, styled, Text } from "tamagui";
3
+ import type { GetProps } from "tamagui";
4
+ import { View, isWeb, styled, Text } from "tamagui";
8
5
  import { t } from "../shared/t";
9
6
  import { fireSelectionHaptic } from "./haptic";
10
- import { WheelEdgeFade } from "./WheelEdgeFade";
11
-
12
- // ── Types ─────────────────────────────────────────────────────
13
-
14
- export interface WheelItem<T = string | number> {
15
- value: T;
16
- label?: string;
17
- }
18
-
19
- /** iOS 14 multi-column highlight joining (CupertinoPicker capStart/capEnd). */
20
- export type WheelHighlightEdges = "all" | "start" | "end" | "none";
21
-
22
- export interface WheelProps<T = string | number> {
23
- /** Array of items to display */
24
- items: (T | WheelItem<T>)[];
25
- /** Currently selected value */
26
- value?: T;
27
- /** Called when selection changes */
28
- onChange?: (value: T) => void;
29
- /** Number of visible items (should be odd for center alignment) */
30
- visibleItems?: 3 | 5 | 7;
31
- /** Height of each item */
32
- itemHeight?: number;
33
- /** Column width in px. Items are absolutely positioned, so this is the painted width. */
34
- width?: number;
35
- /** Custom render function for items */
36
- renderItem?: (item: T, index: number, isSelected: boolean) => ReactNode;
37
- /** Whether the wheel is disabled */
38
- disabled?: boolean;
39
- /** Size token for text */
40
- size?: SizeTokens;
41
- /**
42
- * When true (and there are at least 2 items), scrolling wraps at the ends
43
- * instead of rubber-banding. Uses virtual rows with modulo item indexing.
44
- */
45
- loop?: boolean;
46
- /**
47
- * How the iOS selection fill rounds and insets. `"all"` is a standalone
48
- * column; `"start"` / `"end"` / `"none"` join adjacent columns into one band.
49
- */
50
- highlightEdges?: WheelHighlightEdges;
51
- /** Stretch inside a row of columns (time picker). */
52
- flex?: number;
53
- /**
54
- * Fires once each time a different item crosses the centre band (drag,
55
- * flick, or snap). Native wires this to a selection haptic.
56
- */
57
- onItemCross?: (value: T, index: number) => void;
58
- /** Accessible name for the picker (web renders role="slider"). */
59
- "aria-label"?: string;
60
- }
7
+ import { NativeWheel } from "./NativeWheel";
8
+ import {
9
+ defaultColumnWidth,
10
+ drumPose,
11
+ drumReachRows,
12
+ getItemLabel,
13
+ getItemValue,
14
+ modIndex,
15
+ nearestRowForDataIndex,
16
+ useWheelLayout,
17
+ type WheelProps,
18
+ } from "./wheelShared";
19
+
20
+ export type { WheelHighlightEdges, WheelItem, WheelProps } from "./wheelShared";
61
21
 
62
22
  // ── iOS Physics Constants ─────────────────────────────────────
63
23
  // Based on Apple's UIScrollView deceleration behavior
@@ -74,22 +34,6 @@ type WheelPointer = {
74
34
  locationY?: number;
75
35
  };
76
36
 
77
- /**
78
- * Flutter CupertinoPicker / UIPickerView cylinder. diameterRatio 1.07 and
79
- * squeeze 1.45 are the Cupertino defaults — a shallow drum, not a 70° fan.
80
- */
81
- const diameterRatio = 1.07;
82
- const squeeze = 1.45;
83
- const perspectivePx = 400;
84
- const defaultColumnWidth = 88;
85
-
86
- /** Type scale for wheel numerals — iOS UIPickerView is ~21pt at default size. */
87
- const wheelFontByStep: Record<-1 | 0 | 1, SizeTokens> = {
88
- [-1]: "$6",
89
- 0: "$8",
90
- 1: "$9",
91
- };
92
-
93
37
  // The interactive wrapper is a raw <div>, so the non-removable focus-visible
94
38
  // ring (W12 / DG-A11Y-01 / LC-71: 2px solid $outlineColor) has to come from
95
39
  // an injected stylesheet — inline styles cannot express :focus-visible.
@@ -143,29 +87,6 @@ const SelectionIndicator = styled(View, {
143
87
 
144
88
  // ── Helper functions ──────────────────────────────────────────
145
89
 
146
- function normalizeItem<T>(item: T | WheelItem<T>): WheelItem<T> {
147
- if (typeof item === "object" && item !== null && "value" in item) {
148
- return item as WheelItem<T>;
149
- }
150
- return { value: item as T, label: String(item) };
151
- }
152
-
153
- function getItemLabel<T>(item: T | WheelItem<T>): string {
154
- const normalized = normalizeItem(item);
155
- return normalized.label ?? String(normalized.value);
156
- }
157
-
158
- function getItemValue<T>(item: T | WheelItem<T>): T {
159
- const normalized = normalizeItem(item);
160
- return normalized.value;
161
- }
162
-
163
- /** Non-negative modulo (JavaScript % can be negative for negative lhs). */
164
- function modIndex(row: number, n: number): number {
165
- if (n <= 0) return 0;
166
- return ((row % n) + n) % n;
167
- }
168
-
169
90
  /**
170
91
  * Scroll offset that shows the same item as `targetOffset` (same row mod n), chosen so its
171
92
  * value is closest to `referenceOffset` — shortest motion when animating from `referenceOffset`.
@@ -185,15 +106,6 @@ function equivalentOffsetNear(
185
106
  return base + k * span;
186
107
  }
187
108
 
188
- /** Shortest steps on the item circle from current row to target data index. */
189
- function nearestRowForDataIndex(currentRow: number, targetDataIndex: number, n: number): number {
190
- const currentMod = modIndex(currentRow, n);
191
- let delta = targetDataIndex - currentMod;
192
- if (delta > n / 2) delta -= n;
193
- if (delta < -n / 2) delta += n;
194
- return currentRow + delta;
195
- }
196
-
197
109
  // Rubber-band effect for over-scrolling (iOS style)
198
110
  function rubberBand(offset: number, dimension: number): number {
199
111
  const c = rubberBandConstant;
@@ -203,9 +115,9 @@ function rubberBand(offset: number, dimension: number): number {
203
115
  }
204
116
 
205
117
  /**
206
- * Project a row onto the Cupertino cylinder. Angle 0 sits in the selection
207
- * band; off-center rows recede with rotateX + foreshortening (not opacity —
208
- * row text stays on the AA $color11 ramp).
118
+ * Project a row onto the UIPickerView drum (wheelShared drumPose). Row opacity
119
+ * stays 1 on web: the viewport mask fades the edges, so row text keeps the AA
120
+ * $color11 ramp.
209
121
  */
210
122
  function getItemTransform(
211
123
  index: number,
@@ -213,78 +125,39 @@ function getItemTransform(
213
125
  itemHeight: number,
214
126
  containerHeight: number,
215
127
  ): { translateY: number; rotateX: number; scale: number; opacity: number } {
216
- const radius = (containerHeight * diameterRatio) / 2;
217
- const angle = (index * itemHeight - scrollOffset) / squeeze / Math.max(radius, 1);
218
- const clamped = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, angle));
219
- const centerTop = (containerHeight - itemHeight) / 2;
220
- const translateY = centerTop + radius * Math.sin(clamped);
221
- const rotateX = -(clamped * 180) / Math.PI;
222
- const scale = Math.max(0.72, Math.min(1, Math.cos(clamped)));
128
+ const pose = drumPose(index - scrollOffset / itemHeight, itemHeight, containerHeight);
223
129
  return {
224
- translateY,
225
- rotateX,
226
- scale,
130
+ translateY: (containerHeight - itemHeight) / 2 + pose.offset,
131
+ rotateX: pose.rotateX,
132
+ scale: pose.scale,
227
133
  opacity: 1,
228
134
  };
229
135
  }
230
136
 
231
- function tokenPx(token: string | number, scale: "radius" | "space"): number {
232
- if (typeof token === "number") return token;
233
- const key = String(token).replace(/^\$/, "");
234
- const val = (getTokens() as { [k: string]: Record<string, { val?: number }> })[scale]?.[key]?.val;
235
- if (typeof val === "number") return val;
236
- return scale === "radius" ? 9 : 8;
237
- }
137
+ // ── Wheel Component ───────────────────────────────────────────
238
138
 
239
- function highlightBandStyle(
240
- edges: WheelHighlightEdges,
241
- radiusToken: string | number,
242
- pointy: boolean,
243
- ): CSSProperties {
244
- const r = pointy ? 0 : tokenPx(radiusToken, "radius");
245
- const inset = tokenPx("$2", "space");
246
- const capStart = edges === "all" || edges === "start";
247
- const capEnd = edges === "all" || edges === "end";
248
- return {
249
- borderStartStartRadius: capStart ? r : 0,
250
- borderStartEndRadius: capEnd ? r : 0,
251
- borderEndStartRadius: capStart ? r : 0,
252
- borderEndEndRadius: capEnd ? r : 0,
253
- marginInlineStart: capStart ? inset : 0,
254
- marginInlineEnd: capEnd ? inset : 0,
255
- };
139
+ export function Wheel<T = string | number>(props: WheelProps<T>) {
140
+ return isWeb ? <WebWheel {...props} /> : <NativeWheel {...props} />;
256
141
  }
257
142
 
258
- // ── Wheel Component ───────────────────────────────────────────
259
-
260
- export function Wheel<T = string | number>({
261
- items,
262
- value,
263
- onChange,
264
- visibleItems = 5,
265
- itemHeight: itemHeightProp,
266
- width: widthProp,
267
- renderItem,
268
- disabled = false,
269
- size: sizeProp,
270
- loop = false,
271
- highlightEdges = "all",
272
- flex,
273
- onItemCross,
274
- "aria-label": ariaLabel,
275
- }: WheelProps<T>) {
276
- const { knobProps } = useResolvedKnobs();
277
- const size = sizeProp ?? wheelFontByStep[sizeTokenStep(knobProps.sizeToken)];
278
- // Row geometry rides nestedControl (density-aware, 32px at medium — iOS
279
- // UIPickerView item extent). Explicit itemHeight ejects.
280
- const itemHeight = itemHeightProp ?? knobProps.nestedControl.px ?? 32;
143
+ function WebWheel<T = string | number>(props: WheelProps<T>) {
144
+ const {
145
+ items,
146
+ value,
147
+ onChange,
148
+ visibleItems = 5,
149
+ width: widthProp,
150
+ renderItem,
151
+ disabled = false,
152
+ loop = false,
153
+ flex,
154
+ onItemCross,
155
+ "aria-label": ariaLabel,
156
+ } = props;
157
+ const { knobProps, size, itemHeight, snapDurationMs, highlightStyle, highlightClassName } =
158
+ useWheelLayout(props);
281
159
  const columnWidth = widthProp ?? defaultColumnWidth;
282
160
  ensureWheelFocusStyles();
283
- // LC-25: discrete snaps (keyboard / controlled value) tween; drag stays 1:1.
284
- // knobProps.motion.control.transition is undefined at animation "none" (which also carries
285
- // prefers-reduced-motion) — snaps must JUMP then, not fall back to a tween.
286
- const motionTransition = knobProps.motion.control.transition as string | undefined;
287
- const snapDurationMs = motionDurationMs(motionTransition);
288
161
  const containerRef = useRef<HTMLDivElement>(null);
289
162
  const [scrollOffset, setScrollOffset] = useState(0);
290
163
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -640,51 +513,6 @@ export function Wheel<T = string | number>({
640
513
  [snapToNearestRow, startMomentum, hitTestDataIndex, snapToIndex, disabled],
641
514
  );
642
515
 
643
- const disabledRef = useRef(disabled);
644
- disabledRef.current = disabled;
645
- const pointerDownRef = useRef(handlePointerDown);
646
- pointerDownRef.current = handlePointerDown;
647
- const pointerMoveRef = useRef(handlePointerMove);
648
- pointerMoveRef.current = handlePointerMove;
649
- const pointerUpRef = useRef(handlePointerUp);
650
- pointerUpRef.current = handlePointerUp;
651
-
652
- const panResponder = useMemo(() => {
653
- if (isWeb) return { panHandlers: {} as Record<string, unknown> };
654
- return PanResponder.create({
655
- onStartShouldSetPanResponder: () => !disabledRef.current,
656
- onMoveShouldSetPanResponder: (_, gesture) => Math.abs(gesture.dy) > 1,
657
- onPanResponderTerminationRequest: () => false,
658
- onShouldBlockNativeResponder: () => true,
659
- onPanResponderGrant: (evt: GestureResponderEvent) => {
660
- pointerDownRef.current({
661
- clientY: evt.nativeEvent.pageY,
662
- pointerId: 1,
663
- });
664
- },
665
- onPanResponderMove: (evt: GestureResponderEvent) => {
666
- pointerMoveRef.current({
667
- clientY: evt.nativeEvent.pageY,
668
- pointerId: 1,
669
- });
670
- },
671
- onPanResponderRelease: (evt: GestureResponderEvent) => {
672
- pointerUpRef.current({
673
- clientY: evt.nativeEvent.pageY,
674
- pointerId: 1,
675
- locationY: evt.nativeEvent.locationY,
676
- });
677
- },
678
- onPanResponderTerminate: (evt: GestureResponderEvent) => {
679
- pointerUpRef.current({
680
- clientY: evt.nativeEvent.pageY,
681
- pointerId: 1,
682
- locationY: evt.nativeEvent.locationY,
683
- });
684
- },
685
- });
686
- }, []);
687
-
688
516
  // Handle mouse wheel
689
517
  const handleWheel = useCallback(
690
518
  (e: React.WheelEvent | WheelEvent) => {
@@ -797,36 +625,20 @@ export function Wheel<T = string | number>({
797
625
 
798
626
  const renderRows = useMemo(() => {
799
627
  if (n === 0) return [];
800
- if (loopEnabled) {
801
- const centerRow = Math.round(scrollOffset / itemHeight);
802
- const rowBuffer = paddingItems + 5;
803
- const list: { virtualRow: number; dataIndex: number }[] = [];
804
- for (let vr = centerRow - rowBuffer; vr <= centerRow + rowBuffer; vr++) {
805
- list.push({ virtualRow: vr, dataIndex: modIndex(vr, n) });
806
- }
807
- return list;
628
+ const span = Math.ceil(drumReachRows(itemHeight, containerHeight));
629
+ const centerRow = Math.round(scrollOffset / itemHeight);
630
+ const first = loopEnabled ? centerRow - span : Math.max(0, centerRow - span);
631
+ const last = loopEnabled ? centerRow + span : Math.min(n - 1, centerRow + span);
632
+ const list: { virtualRow: number; dataIndex: number }[] = [];
633
+ for (let vr = first; vr <= last; vr++) {
634
+ list.push({ virtualRow: vr, dataIndex: modIndex(vr, n) });
808
635
  }
809
- const start = Math.max(0, Math.floor(scrollOffset / itemHeight) - paddingItems - 1);
810
- const end = Math.min(
811
- n - 1,
812
- Math.ceil((scrollOffset + containerHeight) / itemHeight) + paddingItems + 1,
813
- );
814
- return Array.from({ length: end - start + 1 }, (_, i) => {
815
- const virtualRow = start + i;
816
- return { virtualRow, dataIndex: virtualRow };
817
- });
818
- }, [n, loopEnabled, scrollOffset, itemHeight, paddingItems, containerHeight]);
636
+ return list;
637
+ }, [n, loopEnabled, scrollOffset, itemHeight, containerHeight]);
819
638
 
820
639
  const itemColor = (isSelected: boolean) =>
821
640
  disabled ? "$color10" : isSelected ? "$color12" : "$color11";
822
641
 
823
- const radiusToken = knobProps.borderRadius.borderRadius as string | number;
824
- const highlightStyle = highlightBandStyle(highlightEdges, radiusToken, Boolean(knobProps.pointy));
825
- const highlightSmoothClass =
826
- "className" in knobProps.borderRadius && typeof knobProps.borderRadius.className === "string"
827
- ? knobProps.borderRadius.className
828
- : undefined;
829
-
830
642
  const renderItemNode = (dataIndex: number, isSelected: boolean) => {
831
643
  const item = items[dataIndex];
832
644
  const itemValue = getItemValue(item);
@@ -859,7 +671,7 @@ export function Wheel<T = string | number>({
859
671
  >
860
672
  {/* iOS 14+ selection fill — LC-71 selected=fill, never hairline borders */}
861
673
  <SelectionIndicator
862
- className={highlightSmoothClass}
674
+ className={highlightClassName}
863
675
  backgroundColor="$color4"
864
676
  top={paddingItems * itemHeight}
865
677
  height={itemHeight}
@@ -868,128 +680,74 @@ export function Wheel<T = string | number>({
868
680
  />
869
681
 
870
682
  {/* Interactive container */}
871
- {isWeb ? (
872
- <div
873
- ref={containerRef}
874
- className="mp1-wheel-wrapper"
875
- tabIndex={disabled ? -1 : 0}
876
- role="slider"
877
- aria-label={ariaLabel ?? t("Value picker")}
878
- aria-orientation="vertical"
879
- aria-valuemin={0}
880
- aria-valuemax={Math.max(0, n - 1)}
881
- aria-valuenow={selectedIndex}
882
- aria-valuetext={
883
- items[selectedIndex] !== undefined ? getItemLabel(items[selectedIndex]) : undefined
884
- }
885
- aria-disabled={disabled || undefined}
886
- data-disabled={disabled ? true : undefined}
887
- style={{
888
- width: "100%",
889
- height: containerHeight,
890
- position: "relative",
891
- perspective: `${perspectivePx}px`,
892
- perspectiveOrigin: "center center",
893
- overflow: "hidden",
894
- userSelect: "none",
895
- touchAction: "none",
896
- cursor: disabled ? "not-allowed" : "grab",
897
- ...wheelMaskStyle,
898
- }}
899
- onPointerDown={handlePointerDown as any}
900
- onPointerMove={handlePointerMove as any}
901
- onPointerUp={handlePointerUp as any}
902
- onPointerCancel={handlePointerUp as any}
903
- onKeyDown={handleKeyDown as any}
904
- data-testid="wheel-wrapper"
905
- >
906
- {renderRows.map(({ virtualRow, dataIndex }) => {
907
- const isSelected = dataIndex === liveDataIndex;
908
- const transform = getItemTransform(
909
- virtualRow,
910
- scrollOffset,
911
- itemHeight,
912
- containerHeight,
913
- );
914
-
915
- if (Math.abs(transform.rotateX) > 85) return null;
916
-
917
- return (
918
- <div
919
- key={`${dataIndex}-${virtualRow}`}
920
- data-testid="wheel-item"
921
- data-index={dataIndex}
922
- data-selected={isSelected ? true : undefined}
923
- aria-hidden={isSelected ? undefined : true}
924
- style={{
925
- position: "absolute",
926
- left: 0,
927
- right: 0,
928
- height: itemHeight,
929
- display: "flex",
930
- alignItems: "center",
931
- justifyContent: "center",
932
- transform: `translateY(${transform.translateY}px) rotateX(${transform.rotateX}deg) scale(${transform.scale})`,
933
- opacity: transform.opacity,
934
- pointerEvents: "none",
935
- transformOrigin: "center center",
936
- transformStyle: "preserve-3d",
937
- backfaceVisibility: "hidden",
938
- }}
939
- >
940
- {renderItemNode(dataIndex, isSelected)}
941
- </div>
942
- );
943
- })}
944
- </div>
945
- ) : (
946
- <View
947
- width="100%"
948
- height={containerHeight}
949
- position="relative"
950
- overflow="hidden"
951
- cursor={disabled ? "not-allowed" : "grab"}
952
- data-testid="wheel-wrapper"
953
- data-disabled={disabled ? true : undefined}
954
- aria-disabled={disabled || undefined}
955
- {...panResponder.panHandlers}
956
- >
957
- {renderRows.map(({ virtualRow, dataIndex }) => {
958
- const isSelected = dataIndex === liveDataIndex;
959
- const transform = getItemTransform(
960
- virtualRow,
961
- scrollOffset,
962
- itemHeight,
963
- containerHeight,
964
- );
965
-
966
- if (Math.abs(transform.rotateX) > 85) return null;
967
-
968
- return (
969
- <View
970
- key={`${dataIndex}-${virtualRow}`}
971
- position="absolute"
972
- left={0}
973
- right={0}
974
- height={itemHeight}
975
- alignItems="center"
976
- justifyContent="center"
977
- opacity={transform.opacity}
978
- pointerEvents="none"
979
- y={transform.translateY}
980
- scale={transform.scale}
981
- rotateX={`${transform.rotateX}deg`}
982
- data-testid="wheel-item"
983
- data-index={dataIndex}
984
- data-selected={isSelected ? true : undefined}
985
- >
986
- {renderItemNode(dataIndex, isSelected)}
987
- </View>
988
- );
989
- })}
990
- <WheelEdgeFade height={paddingItems * itemHeight} />
991
- </View>
992
- )}
683
+ <div
684
+ ref={containerRef}
685
+ className="mp1-wheel-wrapper"
686
+ tabIndex={disabled ? -1 : 0}
687
+ role="slider"
688
+ aria-label={ariaLabel ?? t("Value picker")}
689
+ aria-orientation="vertical"
690
+ aria-valuemin={0}
691
+ aria-valuemax={Math.max(0, n - 1)}
692
+ aria-valuenow={selectedIndex}
693
+ aria-valuetext={
694
+ items[selectedIndex] !== undefined ? getItemLabel(items[selectedIndex]) : undefined
695
+ }
696
+ aria-disabled={disabled || undefined}
697
+ data-disabled={disabled ? true : undefined}
698
+ style={{
699
+ width: "100%",
700
+ height: containerHeight,
701
+ position: "relative",
702
+ perspective: `${containerHeight * 4}px`,
703
+ perspectiveOrigin: "center center",
704
+ overflow: "hidden",
705
+ userSelect: "none",
706
+ touchAction: "none",
707
+ cursor: disabled ? "not-allowed" : "grab",
708
+ ...wheelMaskStyle,
709
+ }}
710
+ onPointerDown={handlePointerDown as any}
711
+ onPointerMove={handlePointerMove as any}
712
+ onPointerUp={handlePointerUp as any}
713
+ onPointerCancel={handlePointerUp as any}
714
+ onKeyDown={handleKeyDown as any}
715
+ data-testid="wheel-wrapper"
716
+ >
717
+ {renderRows.map(({ virtualRow, dataIndex }) => {
718
+ const isSelected = dataIndex === liveDataIndex;
719
+ const transform = getItemTransform(virtualRow, scrollOffset, itemHeight, containerHeight);
720
+
721
+ if (Math.abs(transform.rotateX) > 85) return null;
722
+
723
+ return (
724
+ <div
725
+ key={`${dataIndex}-${virtualRow}`}
726
+ data-testid="wheel-item"
727
+ data-index={dataIndex}
728
+ data-selected={isSelected ? true : undefined}
729
+ aria-hidden={isSelected ? undefined : true}
730
+ style={{
731
+ position: "absolute",
732
+ left: 0,
733
+ right: 0,
734
+ height: itemHeight,
735
+ display: "flex",
736
+ alignItems: "center",
737
+ justifyContent: "center",
738
+ transform: `translateY(${transform.translateY}px) rotateX(${transform.rotateX}deg) scale(${transform.scale})`,
739
+ opacity: transform.opacity,
740
+ pointerEvents: "none",
741
+ transformOrigin: "center center",
742
+ transformStyle: "preserve-3d",
743
+ backfaceVisibility: "hidden",
744
+ }}
745
+ >
746
+ {renderItemNode(dataIndex, isSelected)}
747
+ </div>
748
+ );
749
+ })}
750
+ </div>
993
751
  </WheelContainer>
994
752
  );
995
753
  }
@@ -0,0 +1,53 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { drumPose, drumReachRows } from "./wheelShared";
3
+
4
+ describe("UIPickerView drum geometry", () => {
5
+ const itemHeight = 40;
6
+
7
+ for (const visibleItems of [3, 5, 7]) {
8
+ const frameHeight = itemHeight * visibleItems;
9
+
10
+ it(`never overlaps neighbouring rows (${visibleItems} visible)`, () => {
11
+ const reach = Math.floor(drumReachRows(itemHeight, frameHeight));
12
+ for (let d = -reach; d < reach; d++) {
13
+ const a = drumPose(d, itemHeight, frameHeight);
14
+ const b = drumPose(d + 1, itemHeight, frameHeight);
15
+ const halfA = (itemHeight * Math.cos((a.rotateX * Math.PI) / 180) * a.scale) / 2;
16
+ const halfB = (itemHeight * Math.cos((b.rotateX * Math.PI) / 180) * b.scale) / 2;
17
+ expect(b.offset - a.offset).toBeGreaterThanOrEqual(halfA + halfB - 1e-6);
18
+ }
19
+ });
20
+
21
+ it(`puts the frame edge at the drum's 80° line (${visibleItems} visible)`, () => {
22
+ const reach = drumReachRows(itemHeight, frameHeight);
23
+ for (let d = -reach; d <= reach; d += 0.25) {
24
+ const pose = drumPose(d, itemHeight, frameHeight);
25
+ if (Math.abs(pose.rotateX) > 80) continue;
26
+ expect(Math.abs(pose.offset)).toBeLessThanOrEqual(frameHeight / 2 + 1e-9);
27
+ }
28
+ expect(drumPose(reach + 0.01, itemHeight, frameHeight).opacity).toBe(0);
29
+ });
30
+ }
31
+
32
+ it("seats the centred row flat, full size and opaque", () => {
33
+ expect(drumPose(0, itemHeight, 200)).toEqual({ offset: 0, rotateX: -0, scale: 1, opacity: 1 });
34
+ });
35
+
36
+ it("bends, shrinks and fades rows away from the centre", () => {
37
+ const near = drumPose(1, itemHeight, 200);
38
+ const far = drumPose(3, itemHeight, 200);
39
+ expect(far.rotateX).toBeLessThan(near.rotateX);
40
+ expect(near.rotateX).toBeLessThan(0);
41
+ expect(far.scale).toBeLessThan(near.scale);
42
+ expect(far.opacity).toBeLessThan(near.opacity);
43
+ expect(near.opacity).toBeLessThan(1);
44
+ });
45
+
46
+ it("is symmetric above and below the band", () => {
47
+ const above = drumPose(-2, itemHeight, 200);
48
+ const below = drumPose(2, itemHeight, 200);
49
+ expect(above.offset).toBeCloseTo(-below.offset);
50
+ expect(above.rotateX).toBeCloseTo(-below.rotateX);
51
+ expect(above.opacity).toBeCloseTo(below.opacity);
52
+ });
53
+ });