@multiplatform.one/forms 7.6.2 → 7.7.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 (492) hide show
  1. package/dist/cjs/Button/index.cjs +7 -1
  2. package/dist/cjs/Button/index.native.js +7 -1
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/Chip/index.cjs +1 -3
  5. package/dist/cjs/Chip/index.native.js +5 -3
  6. package/dist/cjs/Chip/index.native.js.map +1 -1
  7. package/dist/cjs/ContextualSaveBar/index.cjs +19 -3
  8. package/dist/cjs/ContextualSaveBar/index.native.js +19 -3
  9. package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
  10. package/dist/cjs/DescriptionList/index.cjs +38 -12
  11. package/dist/cjs/DescriptionList/index.native.js +38 -12
  12. package/dist/cjs/DescriptionList/index.native.js.map +1 -1
  13. package/dist/cjs/ErrorSummary.cjs +4 -1
  14. package/dist/cjs/ErrorSummary.native.js +4 -1
  15. package/dist/cjs/ErrorSummary.native.js.map +1 -1
  16. package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +24 -2
  17. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +43 -2
  18. package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  19. package/dist/cjs/InputParts/index.cjs +42 -2
  20. package/dist/cjs/InputParts/index.native.js +42 -2
  21. package/dist/cjs/InputParts/index.native.js.map +1 -1
  22. package/dist/cjs/fields/Checkbox/Checkbox.cjs +5 -2
  23. package/dist/cjs/fields/Checkbox/Checkbox.native.js +6 -3
  24. package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
  25. package/dist/cjs/fields/Checkbox/CheckboxGroup.cjs +69 -59
  26. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +75 -65
  27. package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  28. package/dist/cjs/fields/Checkbox/checkboxGroupContext.cjs +33 -0
  29. package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js +36 -0
  30. package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
  31. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +402 -0
  32. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +441 -0
  33. package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
  34. package/dist/cjs/fields/DatePicker/DatetimePicker.cjs +2 -376
  35. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +69 -297
  36. package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  37. package/dist/cjs/fields/DatePicker/catalog.cjs +306 -0
  38. package/dist/cjs/fields/DatePicker/catalog.native.js +350 -0
  39. package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -0
  40. package/dist/cjs/fields/DatePicker/index.cjs +7 -280
  41. package/dist/cjs/fields/DatePicker/index.native.js +66 -185
  42. package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
  43. package/dist/cjs/fields/DatePicker/parts.cjs +12 -9
  44. package/dist/cjs/fields/DatePicker/parts.native.js +12 -9
  45. package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
  46. package/dist/cjs/fields/Duration/index.cjs +29 -3
  47. package/dist/cjs/fields/Duration/index.native.js +29 -3
  48. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  49. package/dist/cjs/fields/PhoneInput/index.cjs +9 -4
  50. package/dist/cjs/fields/PhoneInput/index.native.js +13 -6
  51. package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
  52. package/dist/cjs/fields/Rating/index.cjs +1 -1
  53. package/dist/cjs/fields/Rating/index.native.js +4 -1
  54. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  55. package/dist/cjs/fields/SearchInput/index.cjs +6 -0
  56. package/dist/cjs/fields/SearchInput/index.native.js +6 -0
  57. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  58. package/dist/cjs/fields/Select/index.cjs +75 -2
  59. package/dist/cjs/fields/Select/index.native.js +77 -2
  60. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  61. package/dist/cjs/fields/Signature/index.cjs +9 -5
  62. package/dist/cjs/fields/Signature/index.native.js +9 -5
  63. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  64. package/dist/cjs/fields/Slider/index.cjs +3 -1
  65. package/dist/cjs/fields/Slider/index.native.js +3 -1
  66. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  67. package/dist/cjs/fields/Switch/index.cjs +5 -18
  68. package/dist/cjs/fields/Switch/index.native.js +5 -18
  69. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  70. package/dist/cjs/fields/TextArea/index.cjs +19 -5
  71. package/dist/cjs/fields/TextArea/index.native.js +23 -5
  72. package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
  73. package/dist/cjs/fields/TimePicker/catalog.cjs +203 -0
  74. package/dist/cjs/fields/TimePicker/catalog.native.js +227 -0
  75. package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -0
  76. package/dist/cjs/fields/TimePicker/index.cjs +6 -177
  77. package/dist/cjs/fields/TimePicker/index.native.js +74 -85
  78. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  79. package/dist/cjs/fields/TimeRangeScrubber/geometry.cjs +6 -0
  80. package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js +6 -0
  81. package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  82. package/dist/cjs/fields/TimeRangeScrubber/index.cjs +34 -14
  83. package/dist/cjs/fields/TimeRangeScrubber/index.native.js +38 -14
  84. package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
  85. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +202 -0
  86. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +211 -0
  87. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
  88. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.cjs +30 -0
  89. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js +33 -0
  90. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
  91. package/dist/cjs/fields/nativeSelect.cjs +30 -0
  92. package/dist/cjs/fields/nativeSelect.native.js +33 -0
  93. package/dist/cjs/fields/nativeSelect.native.js.map +1 -0
  94. package/dist/cjs/index.cjs +1 -0
  95. package/dist/cjs/index.native.js +1 -0
  96. package/dist/cjs/index.native.js.map +1 -1
  97. package/dist/cjs/themedPrimitives.cjs +5 -0
  98. package/dist/cjs/themedPrimitives.native.js +5 -0
  99. package/dist/cjs/themedPrimitives.native.js.map +1 -1
  100. package/dist/esm/Button/index.mjs +8 -2
  101. package/dist/esm/Button/index.mjs.map +1 -1
  102. package/dist/esm/Button/index.native.js +8 -2
  103. package/dist/esm/Button/index.native.js.map +1 -1
  104. package/dist/esm/Chip/index.mjs +1 -3
  105. package/dist/esm/Chip/index.mjs.map +1 -1
  106. package/dist/esm/Chip/index.native.js +5 -3
  107. package/dist/esm/Chip/index.native.js.map +1 -1
  108. package/dist/esm/ContextualSaveBar/index.mjs +19 -3
  109. package/dist/esm/ContextualSaveBar/index.mjs.map +1 -1
  110. package/dist/esm/ContextualSaveBar/index.native.js +19 -3
  111. package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
  112. package/dist/esm/DescriptionList/index.mjs +38 -12
  113. package/dist/esm/DescriptionList/index.mjs.map +1 -1
  114. package/dist/esm/DescriptionList/index.native.js +38 -12
  115. package/dist/esm/DescriptionList/index.native.js.map +1 -1
  116. package/dist/esm/ErrorSummary.mjs +5 -2
  117. package/dist/esm/ErrorSummary.mjs.map +1 -1
  118. package/dist/esm/ErrorSummary.native.js +5 -2
  119. package/dist/esm/ErrorSummary.native.js.map +1 -1
  120. package/dist/esm/FloatingPanel/useFloatingPanel.mjs +24 -3
  121. package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  122. package/dist/esm/FloatingPanel/useFloatingPanel.native.js +43 -3
  123. package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  124. package/dist/esm/InputParts/index.mjs +43 -3
  125. package/dist/esm/InputParts/index.mjs.map +1 -1
  126. package/dist/esm/InputParts/index.native.js +43 -3
  127. package/dist/esm/InputParts/index.native.js.map +1 -1
  128. package/dist/esm/fields/Checkbox/Checkbox.mjs +5 -2
  129. package/dist/esm/fields/Checkbox/Checkbox.mjs.map +1 -1
  130. package/dist/esm/fields/Checkbox/Checkbox.native.js +6 -3
  131. package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
  132. package/dist/esm/fields/Checkbox/CheckboxGroup.mjs +69 -59
  133. package/dist/esm/fields/Checkbox/CheckboxGroup.mjs.map +1 -1
  134. package/dist/esm/fields/Checkbox/CheckboxGroup.native.js +75 -65
  135. package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  136. package/dist/esm/fields/Checkbox/checkboxGroupContext.mjs +7 -0
  137. package/dist/esm/fields/Checkbox/checkboxGroupContext.mjs.map +1 -0
  138. package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js +7 -0
  139. package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
  140. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +377 -0
  141. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -0
  142. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +413 -0
  143. package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
  144. package/dist/esm/fields/DatePicker/DatetimePicker.mjs +1 -375
  145. package/dist/esm/fields/DatePicker/DatetimePicker.mjs.map +1 -1
  146. package/dist/esm/fields/DatePicker/DatetimePicker.native.js +65 -293
  147. package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  148. package/dist/esm/fields/DatePicker/catalog.mjs +276 -0
  149. package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -0
  150. package/dist/esm/fields/DatePicker/catalog.native.js +317 -0
  151. package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -0
  152. package/dist/esm/fields/DatePicker/index.mjs +1 -274
  153. package/dist/esm/fields/DatePicker/index.mjs.map +1 -1
  154. package/dist/esm/fields/DatePicker/index.native.js +60 -179
  155. package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
  156. package/dist/esm/fields/DatePicker/parts.mjs +13 -10
  157. package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
  158. package/dist/esm/fields/DatePicker/parts.native.js +13 -10
  159. package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
  160. package/dist/esm/fields/Duration/index.mjs +29 -4
  161. package/dist/esm/fields/Duration/index.mjs.map +1 -1
  162. package/dist/esm/fields/Duration/index.native.js +29 -4
  163. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  164. package/dist/esm/fields/PhoneInput/index.mjs +9 -4
  165. package/dist/esm/fields/PhoneInput/index.mjs.map +1 -1
  166. package/dist/esm/fields/PhoneInput/index.native.js +13 -6
  167. package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
  168. package/dist/esm/fields/Rating/index.mjs +2 -2
  169. package/dist/esm/fields/Rating/index.mjs.map +1 -1
  170. package/dist/esm/fields/Rating/index.native.js +5 -2
  171. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  172. package/dist/esm/fields/SearchInput/index.mjs +6 -0
  173. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  174. package/dist/esm/fields/SearchInput/index.native.js +6 -0
  175. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  176. package/dist/esm/fields/Select/index.mjs +76 -3
  177. package/dist/esm/fields/Select/index.mjs.map +1 -1
  178. package/dist/esm/fields/Select/index.native.js +78 -3
  179. package/dist/esm/fields/Select/index.native.js.map +1 -1
  180. package/dist/esm/fields/Signature/index.mjs +10 -6
  181. package/dist/esm/fields/Signature/index.mjs.map +1 -1
  182. package/dist/esm/fields/Signature/index.native.js +10 -6
  183. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  184. package/dist/esm/fields/Slider/index.mjs +4 -2
  185. package/dist/esm/fields/Slider/index.mjs.map +1 -1
  186. package/dist/esm/fields/Slider/index.native.js +4 -2
  187. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  188. package/dist/esm/fields/Switch/index.mjs +6 -19
  189. package/dist/esm/fields/Switch/index.mjs.map +1 -1
  190. package/dist/esm/fields/Switch/index.native.js +6 -19
  191. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  192. package/dist/esm/fields/TextArea/index.mjs +19 -5
  193. package/dist/esm/fields/TextArea/index.mjs.map +1 -1
  194. package/dist/esm/fields/TextArea/index.native.js +23 -5
  195. package/dist/esm/fields/TextArea/index.native.js.map +1 -1
  196. package/dist/esm/fields/TimePicker/catalog.mjs +174 -0
  197. package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -0
  198. package/dist/esm/fields/TimePicker/catalog.native.js +195 -0
  199. package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -0
  200. package/dist/esm/fields/TimePicker/index.mjs +2 -173
  201. package/dist/esm/fields/TimePicker/index.mjs.map +1 -1
  202. package/dist/esm/fields/TimePicker/index.native.js +76 -87
  203. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  204. package/dist/esm/fields/TimeRangeScrubber/geometry.mjs +6 -1
  205. package/dist/esm/fields/TimeRangeScrubber/geometry.mjs.map +1 -1
  206. package/dist/esm/fields/TimeRangeScrubber/geometry.native.js +6 -1
  207. package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  208. package/dist/esm/fields/TimeRangeScrubber/index.mjs +37 -18
  209. package/dist/esm/fields/TimeRangeScrubber/index.mjs.map +1 -1
  210. package/dist/esm/fields/TimeRangeScrubber/index.native.js +41 -18
  211. package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
  212. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +166 -0
  213. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -0
  214. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +172 -0
  215. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
  216. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs +5 -0
  217. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -0
  218. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js +5 -0
  219. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
  220. package/dist/esm/fields/nativeSelect.mjs +5 -0
  221. package/dist/esm/fields/nativeSelect.mjs.map +1 -0
  222. package/dist/esm/fields/nativeSelect.native.js +5 -0
  223. package/dist/esm/fields/nativeSelect.native.js.map +1 -0
  224. package/dist/esm/index.mjs +2 -2
  225. package/dist/esm/index.mjs.map +1 -1
  226. package/dist/esm/index.native.js +2 -2
  227. package/dist/esm/index.native.js.map +1 -1
  228. package/dist/esm/themedPrimitives.mjs +5 -0
  229. package/dist/esm/themedPrimitives.mjs.map +1 -1
  230. package/dist/esm/themedPrimitives.native.js +5 -0
  231. package/dist/esm/themedPrimitives.native.js.map +1 -1
  232. package/dist/jsx/Button/index.mjs +8 -2
  233. package/dist/jsx/Button/index.mjs.map +1 -1
  234. package/dist/jsx/Button/index.native.js +7 -1
  235. package/dist/jsx/Button/index.native.js.map +1 -1
  236. package/dist/jsx/Chip/index.mjs +1 -3
  237. package/dist/jsx/Chip/index.mjs.map +1 -1
  238. package/dist/jsx/Chip/index.native.js +5 -3
  239. package/dist/jsx/Chip/index.native.js.map +1 -1
  240. package/dist/jsx/ContextualSaveBar/index.mjs +19 -3
  241. package/dist/jsx/ContextualSaveBar/index.mjs.map +1 -1
  242. package/dist/jsx/ContextualSaveBar/index.native.js +19 -3
  243. package/dist/jsx/ContextualSaveBar/index.native.js.map +1 -1
  244. package/dist/jsx/DescriptionList/index.mjs +38 -12
  245. package/dist/jsx/DescriptionList/index.mjs.map +1 -1
  246. package/dist/jsx/DescriptionList/index.native.js +38 -12
  247. package/dist/jsx/DescriptionList/index.native.js.map +1 -1
  248. package/dist/jsx/ErrorSummary.mjs +5 -2
  249. package/dist/jsx/ErrorSummary.mjs.map +1 -1
  250. package/dist/jsx/ErrorSummary.native.js +4 -1
  251. package/dist/jsx/ErrorSummary.native.js.map +1 -1
  252. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +24 -3
  253. package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
  254. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +43 -2
  255. package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
  256. package/dist/jsx/InputParts/index.mjs +43 -3
  257. package/dist/jsx/InputParts/index.mjs.map +1 -1
  258. package/dist/jsx/InputParts/index.native.js +42 -2
  259. package/dist/jsx/InputParts/index.native.js.map +1 -1
  260. package/dist/jsx/fields/Checkbox/Checkbox.mjs +5 -2
  261. package/dist/jsx/fields/Checkbox/Checkbox.mjs.map +1 -1
  262. package/dist/jsx/fields/Checkbox/Checkbox.native.js +6 -3
  263. package/dist/jsx/fields/Checkbox/Checkbox.native.js.map +1 -1
  264. package/dist/jsx/fields/Checkbox/CheckboxGroup.mjs +69 -59
  265. package/dist/jsx/fields/Checkbox/CheckboxGroup.mjs.map +1 -1
  266. package/dist/jsx/fields/Checkbox/CheckboxGroup.native.js +75 -65
  267. package/dist/jsx/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
  268. package/dist/jsx/fields/Checkbox/checkboxGroupContext.mjs +7 -0
  269. package/dist/jsx/fields/Checkbox/checkboxGroupContext.mjs.map +1 -0
  270. package/dist/jsx/fields/Checkbox/checkboxGroupContext.native.js +36 -0
  271. package/dist/jsx/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
  272. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +377 -0
  273. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -0
  274. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +441 -0
  275. package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
  276. package/dist/jsx/fields/DatePicker/DatetimePicker.mjs +1 -375
  277. package/dist/jsx/fields/DatePicker/DatetimePicker.mjs.map +1 -1
  278. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +69 -297
  279. package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
  280. package/dist/jsx/fields/DatePicker/catalog.mjs +276 -0
  281. package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -0
  282. package/dist/jsx/fields/DatePicker/catalog.native.js +350 -0
  283. package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -0
  284. package/dist/jsx/fields/DatePicker/index.mjs +1 -274
  285. package/dist/jsx/fields/DatePicker/index.mjs.map +1 -1
  286. package/dist/jsx/fields/DatePicker/index.native.js +66 -185
  287. package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
  288. package/dist/jsx/fields/DatePicker/parts.mjs +13 -10
  289. package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
  290. package/dist/jsx/fields/DatePicker/parts.native.js +12 -9
  291. package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
  292. package/dist/jsx/fields/Duration/index.mjs +29 -4
  293. package/dist/jsx/fields/Duration/index.mjs.map +1 -1
  294. package/dist/jsx/fields/Duration/index.native.js +29 -3
  295. package/dist/jsx/fields/Duration/index.native.js.map +1 -1
  296. package/dist/jsx/fields/PhoneInput/index.mjs +9 -4
  297. package/dist/jsx/fields/PhoneInput/index.mjs.map +1 -1
  298. package/dist/jsx/fields/PhoneInput/index.native.js +13 -6
  299. package/dist/jsx/fields/PhoneInput/index.native.js.map +1 -1
  300. package/dist/jsx/fields/Rating/index.mjs +2 -2
  301. package/dist/jsx/fields/Rating/index.mjs.map +1 -1
  302. package/dist/jsx/fields/Rating/index.native.js +4 -1
  303. package/dist/jsx/fields/Rating/index.native.js.map +1 -1
  304. package/dist/jsx/fields/SearchInput/index.mjs +6 -0
  305. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  306. package/dist/jsx/fields/SearchInput/index.native.js +6 -0
  307. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  308. package/dist/jsx/fields/Select/index.mjs +76 -3
  309. package/dist/jsx/fields/Select/index.mjs.map +1 -1
  310. package/dist/jsx/fields/Select/index.native.js +77 -2
  311. package/dist/jsx/fields/Select/index.native.js.map +1 -1
  312. package/dist/jsx/fields/Signature/index.mjs +10 -6
  313. package/dist/jsx/fields/Signature/index.mjs.map +1 -1
  314. package/dist/jsx/fields/Signature/index.native.js +9 -5
  315. package/dist/jsx/fields/Signature/index.native.js.map +1 -1
  316. package/dist/jsx/fields/Slider/index.mjs +4 -2
  317. package/dist/jsx/fields/Slider/index.mjs.map +1 -1
  318. package/dist/jsx/fields/Slider/index.native.js +3 -1
  319. package/dist/jsx/fields/Slider/index.native.js.map +1 -1
  320. package/dist/jsx/fields/Switch/index.mjs +6 -19
  321. package/dist/jsx/fields/Switch/index.mjs.map +1 -1
  322. package/dist/jsx/fields/Switch/index.native.js +5 -18
  323. package/dist/jsx/fields/Switch/index.native.js.map +1 -1
  324. package/dist/jsx/fields/TextArea/index.mjs +19 -5
  325. package/dist/jsx/fields/TextArea/index.mjs.map +1 -1
  326. package/dist/jsx/fields/TextArea/index.native.js +23 -5
  327. package/dist/jsx/fields/TextArea/index.native.js.map +1 -1
  328. package/dist/jsx/fields/TimePicker/catalog.mjs +174 -0
  329. package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -0
  330. package/dist/jsx/fields/TimePicker/catalog.native.js +227 -0
  331. package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -0
  332. package/dist/jsx/fields/TimePicker/index.mjs +2 -173
  333. package/dist/jsx/fields/TimePicker/index.mjs.map +1 -1
  334. package/dist/jsx/fields/TimePicker/index.native.js +74 -85
  335. package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
  336. package/dist/jsx/fields/TimeRangeScrubber/geometry.mjs +6 -1
  337. package/dist/jsx/fields/TimeRangeScrubber/geometry.mjs.map +1 -1
  338. package/dist/jsx/fields/TimeRangeScrubber/geometry.native.js +6 -0
  339. package/dist/jsx/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
  340. package/dist/jsx/fields/TimeRangeScrubber/index.mjs +37 -18
  341. package/dist/jsx/fields/TimeRangeScrubber/index.mjs.map +1 -1
  342. package/dist/jsx/fields/TimeRangeScrubber/index.native.js +38 -14
  343. package/dist/jsx/fields/TimeRangeScrubber/index.native.js.map +1 -1
  344. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +166 -0
  345. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -0
  346. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +211 -0
  347. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
  348. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs +5 -0
  349. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -0
  350. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js +33 -0
  351. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
  352. package/dist/jsx/fields/nativeSelect.mjs +5 -0
  353. package/dist/jsx/fields/nativeSelect.mjs.map +1 -0
  354. package/dist/jsx/fields/nativeSelect.native.js +33 -0
  355. package/dist/jsx/fields/nativeSelect.native.js.map +1 -0
  356. package/dist/jsx/index.js +2 -2
  357. package/dist/jsx/index.js.map +1 -1
  358. package/dist/jsx/index.mjs +2 -2
  359. package/dist/jsx/index.mjs.map +1 -1
  360. package/dist/jsx/index.native.js +1 -0
  361. package/dist/jsx/index.native.js.map +1 -1
  362. package/dist/jsx/themedPrimitives.mjs +5 -0
  363. package/dist/jsx/themedPrimitives.mjs.map +1 -1
  364. package/dist/jsx/themedPrimitives.native.js +5 -0
  365. package/dist/jsx/themedPrimitives.native.js.map +1 -1
  366. package/package.json +12 -9
  367. package/src/Button/Button.labelInk.spec.tsx +1 -1
  368. package/src/Button/Button.sizeLadder.spec.tsx +124 -0
  369. package/src/Button/Button.spec.tsx +7 -17
  370. package/src/Button/Button.stories.tsx +14 -0
  371. package/src/Button/index.tsx +18 -3
  372. package/src/Chip/Chip.spec.tsx +15 -0
  373. package/src/Chip/Chip.stories.tsx +251 -0
  374. package/src/Chip/index.tsx +5 -1
  375. package/src/ContextualSaveBar/ContextualSaveBar.spec.tsx +51 -0
  376. package/src/ContextualSaveBar/ContextualSaveBar.stories.tsx +42 -23
  377. package/src/ContextualSaveBar/UnsavedChangesDialog.spec.tsx +167 -0
  378. package/src/ContextualSaveBar/index.tsx +14 -5
  379. package/src/DescriptionList/DescriptionList.spec.tsx +19 -1
  380. package/src/DescriptionList/DescriptionList.stories.tsx +5 -1
  381. package/src/DescriptionList/index.tsx +57 -19
  382. package/src/ErrorSummary.tsx +5 -2
  383. package/src/FloatingPanel/panelWidthContract.spec.ts +2 -2
  384. package/src/FloatingPanel/useFloatingPanel.spec.tsx +199 -1
  385. package/src/FloatingPanel/useFloatingPanel.ts +45 -3
  386. package/src/InputParts/index.tsx +56 -2
  387. package/src/Spinner/SpinnerGlyph.spec.tsx +132 -0
  388. package/src/Wheel/WheelEdgeFade.spec.tsx +141 -0
  389. package/src/fields/Checkbox/Checkbox.spec.tsx +24 -0
  390. package/src/fields/Checkbox/Checkbox.tsx +8 -3
  391. package/src/fields/Checkbox/CheckboxBox.spec.tsx +395 -0
  392. package/src/fields/Checkbox/CheckboxCard.spec.tsx +197 -0
  393. package/src/fields/Checkbox/CheckboxFieldFrame.spec.tsx +116 -0
  394. package/src/fields/Checkbox/CheckboxGroup.stories.tsx +15 -0
  395. package/src/fields/Checkbox/CheckboxGroup.tsx +77 -70
  396. package/src/fields/Checkbox/checkboxGroupContext.ts +12 -0
  397. package/src/fields/DatePicker/Calendar.spec.tsx +6 -5
  398. package/src/fields/DatePicker/DatePicker.stories.tsx +13 -0
  399. package/src/fields/DatePicker/DatetimePicker.catalog.tsx +586 -0
  400. package/src/fields/DatePicker/DatetimePicker.native.tsx +243 -0
  401. package/src/fields/DatePicker/DatetimePicker.tsx +2 -584
  402. package/src/fields/DatePicker/catalog.tsx +450 -0
  403. package/src/fields/DatePicker/index.native.tsx +261 -0
  404. package/src/fields/DatePicker/index.tsx +20 -448
  405. package/src/fields/DatePicker/parts.tsx +11 -6
  406. package/src/fields/Duration/Duration.spec.tsx +50 -0
  407. package/src/fields/Duration/Duration.stories.tsx +21 -0
  408. package/src/fields/Duration/index.tsx +43 -5
  409. package/src/fields/PhoneInput/PhoneInput.spec.tsx +16 -63
  410. package/src/fields/PhoneInput/PhoneInput.tvalue-source.spec.ts +23 -0
  411. package/src/fields/PhoneInput/index.tsx +10 -3
  412. package/src/fields/Rating/index.tsx +9 -1
  413. package/src/fields/SearchInput/SearchInput.spec.tsx +20 -0
  414. package/src/fields/SearchInput/index.tsx +6 -0
  415. package/src/fields/Select/Select.stories.tsx +23 -1
  416. package/src/fields/Select/index.tsx +77 -1
  417. package/src/fields/Signature/Signature.spec.tsx +22 -3
  418. package/src/fields/Signature/index.tsx +16 -7
  419. package/src/fields/Slider/index.tsx +4 -1
  420. package/src/fields/Switch/index.tsx +14 -12
  421. package/src/fields/TextArea/TextArea.spec.tsx +68 -0
  422. package/src/fields/TextArea/index.tsx +23 -5
  423. package/src/fields/TimePicker/TimePicker.stories.tsx +13 -0
  424. package/src/fields/TimePicker/catalog.tsx +265 -0
  425. package/src/fields/TimePicker/index.native.tsx +229 -0
  426. package/src/fields/TimePicker/index.tsx +6 -261
  427. package/src/fields/TimeRangeScrubber/TimeRangeScrubber.spec.tsx +36 -0
  428. package/src/fields/TimeRangeScrubber/geometry.ts +13 -0
  429. package/src/fields/TimeRangeScrubber/index.tsx +49 -8
  430. package/src/fields/nativeDateTime/OsDateTimeSheet.spec.ts +12 -0
  431. package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +197 -0
  432. package/src/fields/nativeDateTime/osDateTimeIosDisplay.ts +4 -0
  433. package/src/fields/nativeSelect.spec.ts +28 -0
  434. package/src/fields/nativeSelect.ts +13 -0
  435. package/src/index.ts +2 -2
  436. package/src/themedPrimitives.tsx +10 -6
  437. package/types/Button/index.d.ts +3 -2
  438. package/types/Button/index.d.ts.map +1 -1
  439. package/types/Chip/index.d.ts.map +1 -1
  440. package/types/ContextualSaveBar/index.d.ts.map +1 -1
  441. package/types/DescriptionList/index.d.ts.map +1 -1
  442. package/types/ErrorSummary.d.ts.map +1 -1
  443. package/types/FloatingPanel/useFloatingPanel.d.ts +13 -0
  444. package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
  445. package/types/InputParts/index.d.ts.map +1 -1
  446. package/types/fields/Checkbox/Checkbox.d.ts.map +1 -1
  447. package/types/fields/Checkbox/CheckboxGroup.d.ts.map +1 -1
  448. package/types/fields/Checkbox/checkboxGroupContext.d.ts +8 -0
  449. package/types/fields/Checkbox/checkboxGroupContext.d.ts.map +1 -0
  450. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +35 -0
  451. package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -0
  452. package/types/fields/DatePicker/DatetimePicker.d.ts +2 -32
  453. package/types/fields/DatePicker/DatetimePicker.d.ts.map +1 -1
  454. package/types/fields/DatePicker/DatetimePicker.native.d.ts +26 -0
  455. package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -0
  456. package/types/fields/DatePicker/catalog.d.ts +42 -0
  457. package/types/fields/DatePicker/catalog.d.ts.map +1 -0
  458. package/types/fields/DatePicker/index.d.ts +2 -39
  459. package/types/fields/DatePicker/index.d.ts.map +1 -1
  460. package/types/fields/DatePicker/index.native.d.ts +40 -0
  461. package/types/fields/DatePicker/index.native.d.ts.map +1 -0
  462. package/types/fields/DatePicker/parts.d.ts.map +1 -1
  463. package/types/fields/Duration/index.d.ts +2 -0
  464. package/types/fields/Duration/index.d.ts.map +1 -1
  465. package/types/fields/PhoneInput/index.d.ts.map +1 -1
  466. package/types/fields/Rating/index.d.ts.map +1 -1
  467. package/types/fields/SearchInput/index.d.ts.map +1 -1
  468. package/types/fields/Select/index.d.ts +6 -1
  469. package/types/fields/Select/index.d.ts.map +1 -1
  470. package/types/fields/Signature/index.d.ts.map +1 -1
  471. package/types/fields/Slider/index.d.ts.map +1 -1
  472. package/types/fields/Switch/index.d.ts.map +1 -1
  473. package/types/fields/TextArea/index.d.ts.map +1 -1
  474. package/types/fields/TimePicker/catalog.d.ts +41 -0
  475. package/types/fields/TimePicker/catalog.d.ts.map +1 -0
  476. package/types/fields/TimePicker/index.d.ts +2 -38
  477. package/types/fields/TimePicker/index.d.ts.map +1 -1
  478. package/types/fields/TimePicker/index.native.d.ts +31 -0
  479. package/types/fields/TimePicker/index.native.d.ts.map +1 -0
  480. package/types/fields/TimeRangeScrubber/geometry.d.ts +8 -0
  481. package/types/fields/TimeRangeScrubber/geometry.d.ts.map +1 -1
  482. package/types/fields/TimeRangeScrubber/index.d.ts +1 -1
  483. package/types/fields/TimeRangeScrubber/index.d.ts.map +1 -1
  484. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts +12 -0
  485. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -0
  486. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts +3 -0
  487. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts.map +1 -0
  488. package/types/fields/nativeSelect.d.ts +12 -0
  489. package/types/fields/nativeSelect.d.ts.map +1 -0
  490. package/types/index.d.ts +2 -2
  491. package/types/index.d.ts.map +1 -1
  492. package/types/themedPrimitives.d.ts.map +1 -1
@@ -1,263 +1,8 @@
1
- import type { ReactNode } from "react";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
3
- import type { LabelProps, SizeTokens } from "tamagui";
4
- import { isWeb } from "tamagui";
5
- import { Skeleton } from "../../Skeleton";
6
- import { Field, FieldLayout } from "../../fieldLayout";
7
- import { FloatingPanel } from "../../FloatingPanel";
8
- import { Input as InputParts } from "../../InputParts";
9
- import { t } from "../../shared/t";
10
- import {
11
- getFieldError,
12
- mergeFieldHandler,
13
- useFormField,
14
- useResolvedValidators,
15
- } from "../../shared/utils";
16
- import type { AnyFormApi } from "../../types";
17
- import {
18
- TimePanel,
19
- TimePickerClearButton,
20
- TimePickerInputTrigger,
1
+ export {
2
+ TimePicker,
21
3
  formatTimeDisplay,
22
4
  isValidTimeString,
23
- type TimeFormat,
24
- } from "./parts";
25
-
26
- export type { TimeFormat } from "./parts";
27
- export { formatTimeDisplay, isValidTimeString, parseTimeString, toTimeString } from "./parts";
28
-
29
- export interface TimePickerProps {
30
- name?: string;
31
- form?: AnyFormApi;
32
- validators?: Record<string, unknown>;
33
- label?: ReactNode;
34
- labelProps?: Omit<LabelProps, "htmlFor" | "ref">;
35
- helperText?: string;
36
- error?: string | boolean;
37
- required?: boolean;
38
- size?: SizeTokens;
39
- id?: string;
40
- /** Time string in "HH:MM" or "HH:MM:SS" 24h format */
41
- value?: string;
42
- defaultValue?: string;
43
- onChange?: (value: string) => void;
44
- onBlur?: (...args: any[]) => void;
45
- disabled?: boolean;
46
- readOnly?: boolean;
47
- /** Display format (default "12h") */
48
- timeFormat?: TimeFormat;
49
- /** Minute step increment (default 5) */
50
- minuteStep?: number;
51
- /** Show seconds column (default false) */
52
- showSeconds?: boolean;
53
- placeholder?: string;
54
- clockIcon?: ReactNode;
55
- /** When true, renders a skeleton placeholder instead of the time picker */
56
- skeleton?: boolean;
57
- /** When true, applies compact density (tighter layout gaps; control size unchanged) */
58
- compact?: boolean;
59
- }
60
-
61
- export function TimePicker({
62
- name,
63
- form: formProp,
64
- validators,
65
- label,
66
- labelProps,
67
- helperText,
68
- error,
69
- required,
70
- size,
71
- id: idProp,
72
- value: valueProp,
73
- defaultValue,
74
- onChange,
75
- onBlur,
76
- disabled,
77
- readOnly,
78
- timeFormat = "12h",
79
- minuteStep = 5,
80
- showSeconds = false,
81
- placeholder = t("Select time"),
82
- clockIcon,
83
- skeleton,
84
- compact,
85
- }: TimePickerProps) {
86
- const { resolvedForm, knobProps, id } = useFormField({
87
- form: formProp,
88
- id: idProp,
89
- compact,
90
- });
91
- const resolvedValidators = useResolvedValidators(required, validators, label, name);
92
- const [open, setOpen] = useState(false);
93
- const [internalValue, setInternalValue] = useState<string>(() => valueProp ?? defaultValue ?? "");
94
- useEffect(() => {
95
- if (valueProp === undefined) return;
96
- setInternalValue(valueProp);
97
- }, [valueProp]);
98
- const value = internalValue;
99
- const valueIsInvalid = !!value.trim() && !isValidTimeString(value);
100
- const formatError = valueIsInvalid ? t("Invalid time value") : undefined;
101
-
102
- const displayValue = useMemo(
103
- () => (valueIsInvalid ? "" : formatTimeDisplay(value, timeFormat, showSeconds)),
104
- [value, valueIsInvalid, timeFormat, showSeconds],
105
- );
106
-
107
- const handleChange = useCallback(
108
- (time: string, fieldChange?: (v: any) => void) => {
109
- setInternalValue(time);
110
- fieldChange?.(time);
111
- onChange?.(time);
112
- },
113
- [onChange],
114
- );
115
-
116
- const handleClear = useCallback(
117
- (fieldChange?: (v: any) => void) => {
118
- handleChange("", fieldChange);
119
- },
120
- [handleChange],
121
- );
122
-
123
- const renderTrigger = (
124
- triggerValue: string,
125
- hasError?: boolean,
126
- fieldChange?: (v: any) => void,
127
- readonly?: boolean,
128
- ) => (
129
- <TimePickerInputTrigger
130
- value={triggerValue}
131
- placeholder={placeholder}
132
- disabled={disabled}
133
- error={hasError}
134
- id={id}
135
- clockIcon={clockIcon}
136
- onOpen={() => setOpen(true)}
137
- onClear={() => handleClear(fieldChange)}
138
- aria-label={label ? undefined : t("Time")}
139
- aria-readonly={readonly || undefined}
140
- aria-required={required || undefined}
141
- aria-invalid={hasError || undefined}
142
- />
143
- );
144
-
145
- const panelContent = (currentValue: string, onValueChange: (next: string) => void) => (
146
- <TimePanel
147
- value={currentValue || "00:00"}
148
- onChange={onValueChange}
149
- timeFormat={timeFormat}
150
- minuteStep={minuteStep}
151
- showSeconds={showSeconds}
152
- />
153
- );
154
-
155
- if (skeleton) {
156
- return (
157
- <FieldLayout id={id} label={label} size={size} knobProps={knobProps}>
158
- <Skeleton variant="rounded" width="100%" height={knobProps.sizeToken} />
159
- </FieldLayout>
160
- );
161
- }
162
-
163
- const renderField = (
164
- triggerValue: string,
165
- hasError?: boolean,
166
- blurHandler?: (...args: any[]) => void,
167
- fieldChange?: (v: any) => void,
168
- fieldValue?: string,
169
- ) => {
170
- if (readOnly) {
171
- return renderTrigger(triggerValue, hasError, undefined, true);
172
- }
173
- return (
174
- <FloatingPanel
175
- open={open}
176
- onOpenChange={(isOpen) => {
177
- setOpen(isOpen);
178
- if (!isOpen) blurHandler?.();
179
- }}
180
- disabled={disabled}
181
- sizing="grow"
182
- widthMode="at-least-trigger"
183
- triggerA11y={{
184
- label: (typeof label === "string" && label) || t("Time"),
185
- value: triggerValue || placeholder,
186
- hint: typeof helperText === "string" ? helperText : undefined,
187
- }}
188
- trigger={renderTrigger(triggerValue, hasError, fieldChange)}
189
- triggerEnd={
190
- !isWeb && triggerValue ? (
191
- <TimePickerClearButton onClear={() => handleClear(fieldChange)} disabled={disabled} />
192
- ) : undefined
193
- }
194
- >
195
- {panelContent(fieldValue ?? value, (time) => handleChange(time, fieldChange))}
196
- </FloatingPanel>
197
- );
198
- };
199
-
200
- if (!resolvedForm || !name) {
201
- return (
202
- <FieldLayout
203
- id={id}
204
- label={label}
205
- labelProps={labelProps}
206
- error={error || formatError}
207
- helperText={helperText}
208
- required={required}
209
- size={size}
210
- knobProps={knobProps}
211
- onBlur={onBlur}
212
- disabled={disabled}
213
- >
214
- <InputParts size={size || knobProps.sizeToken}>
215
- {renderField(displayValue, !!error || valueIsInvalid, onBlur)}
216
- </InputParts>
217
- </FieldLayout>
218
- );
219
- }
220
-
221
- return (
222
- <Field
223
- form={resolvedForm}
224
- name={name}
225
- defaultValue={defaultValue}
226
- validators={resolvedValidators}
227
- >
228
- {(field) => {
229
- const resolvedError = getFieldError(field, error);
230
- const fieldValue = (field.state.value as string) ?? "";
231
- const fieldIsInvalid = !!fieldValue.trim() && !isValidTimeString(fieldValue);
232
- const fieldFormatError = fieldIsInvalid ? t("Invalid time value") : undefined;
233
- const fieldDisplay = fieldIsInvalid
234
- ? ""
235
- : formatTimeDisplay(fieldValue, timeFormat, showSeconds);
236
- return (
237
- <FieldLayout
238
- id={id}
239
- label={label}
240
- labelProps={labelProps}
241
- error={resolvedError || fieldFormatError}
242
- helperText={helperText}
243
- required={required}
244
- size={size}
245
- knobProps={knobProps}
246
- onBlur={mergeFieldHandler(field, "handleBlur", onBlur)}
247
- disabled={disabled}
248
- >
249
- <InputParts size={size || knobProps.sizeToken}>
250
- {renderField(
251
- fieldDisplay,
252
- !!resolvedError || fieldIsInvalid,
253
- mergeFieldHandler(field, "handleBlur", onBlur),
254
- (v) => field.handleChange(v),
255
- fieldValue,
256
- )}
257
- </InputParts>
258
- </FieldLayout>
259
- );
260
- }}
261
- </Field>
262
- );
263
- }
5
+ parseTimeString,
6
+ toTimeString,
7
+ } from "./catalog";
8
+ export type { TimeFormat, TimePickerProps } from "./catalog";
@@ -13,6 +13,7 @@ import {
13
13
  DEFAULT_QUICK_RANGES,
14
14
  applyQuickRange,
15
15
  assertAbsoluteDateRange,
16
+ clampCapLeft,
16
17
  dateRangeKeys,
17
18
  fractionToTime,
18
19
  getScrubberGripRenderSize,
@@ -150,6 +151,14 @@ describe("TimeRangeScrubber geometry", () => {
150
151
  expect(resolveScrubberTransition("jump", "quick", true)).toBe("none");
151
152
  expect(resolveScrubberTransition("jump", undefined, false)).toBe("none");
152
153
  });
154
+
155
+ it("clamps the playhead cap inside the rail, including at the right edge", () => {
156
+ expect(clampCapLeft(0, 400, 79.6)).toBe(0);
157
+ expect(clampCapLeft(1, 400, 79.6)).toBeCloseTo(320.4);
158
+ expect(clampCapLeft(0.5, 400, 80)).toBe(160);
159
+ expect(clampCapLeft(1, 400, 79.6)).toBeLessThanOrEqual(400 - 79.6);
160
+ expect(clampCapLeft(0.5, 0, 80)).toBeNull();
161
+ });
153
162
  });
154
163
 
155
164
  describe("TimeRangeScrubber", () => {
@@ -183,6 +192,33 @@ describe("TimeRangeScrubber", () => {
183
192
  expect(result.container.querySelector("[data-part='activity']")).toBeTruthy();
184
193
  });
185
194
 
195
+ it("keeps the 28px playhead cap to a single clock line; frame stale sits outside", () => {
196
+ const scrub = new Date("2026-08-28T14:22:31.000Z");
197
+ const result = renderWithProviders(
198
+ <TimeRangeScrubber
199
+ label="Session"
200
+ now={NOW}
201
+ window={WINDOW}
202
+ scrubTime={scrub}
203
+ frames={[new Date("2026-08-28T14:22:00.000Z")]}
204
+ value={{
205
+ start: new Date("2026-08-28T14:17:30.000Z"),
206
+ end: new Date("2026-08-28T14:27:30.000Z"),
207
+ }}
208
+ />,
209
+ );
210
+ const cap = result.container.querySelector("[data-part='cap']");
211
+ expect(cap).toBeTruthy();
212
+ expect(cap?.textContent?.trim()).toMatch(/^\d{2}:\d{2}:\d{2}$/);
213
+ expect(cap?.textContent).not.toMatch(/frame/i);
214
+ const nested = cap?.querySelector("[data-part='frame-stale']");
215
+ expect(nested).toBeFalsy();
216
+ const stale = result.container.querySelector("[data-part='frame-stale']");
217
+ expect(stale).toBeTruthy();
218
+ expect(stale?.textContent).toMatch(/frame/i);
219
+ expect(cap?.contains(stale)).toBe(false);
220
+ });
221
+
186
222
  it("paints the grip 0 at borderRadius:none and h/2 at medium", () => {
187
223
  const none = renderWithProviders(
188
224
  <Preset overrides={{ borderRadius: "none" }}>
@@ -17,8 +17,10 @@ export const BRUSH_HANDLE = { width: 7, height: 28 } as const;
17
17
  export const BRUSH_BAND_HEIGHT = 28;
18
18
  export const PLAYHEAD_WIDTH = 1;
19
19
  export const CHIP_HEIGHT = 28;
20
+ /** Board-measured pin (D-scrubber 28/r5). Family vs per-part radius is RULE MISSING. */
20
21
  export const CHIP_RADIUS = 5;
21
22
  export const CAP_HEIGHT = 28;
23
+ /** Board-measured pin (D-scrubber 28/r9). Family vs per-part radius is RULE MISSING. */
22
24
  export const CAP_RADIUS = 9;
23
25
 
24
26
  /** LC-43 recognition constant: touch must hold before a lift, so swipe-scroll survives. */
@@ -142,6 +144,17 @@ export function timeToFraction(time: Date, window: TimeWindow): number {
142
144
  return Math.min(1, Math.max(0, (time.getTime() - window.start.getTime()) / span));
143
145
  }
144
146
 
147
+ /**
148
+ * Pixel left for a playhead cap so it stays inside the rail.
149
+ * Centering with `x="-50%"` at fraction=1 hangs by half the cap (the 39.8px
150
+ * spill on a 79.6px / 28px pill).
151
+ */
152
+ export function clampCapLeft(fraction: number, railWidth: number, capWidth: number): number | null {
153
+ if (railWidth <= 0 || capWidth <= 0) return null;
154
+ const center = fraction * railWidth;
155
+ return Math.min(Math.max(center - capWidth / 2, 0), Math.max(0, railWidth - capWidth));
156
+ }
157
+
145
158
  export function fractionToTime(fraction: number, window: TimeWindow): Date {
146
159
  const span = window.end.getTime() - window.start.getTime();
147
160
  const clamped = Math.min(1, Math.max(0, fraction));
@@ -2,6 +2,7 @@ import {
2
2
  defaultKnobs,
3
3
  ensureFocusVisibleRing,
4
4
  ensureKeyboardModalityTracking,
5
+ FOCUS_RING_HALO_OFFSET,
5
6
  formatAbsoluteDateTime,
6
7
  pressTargetHitSlop,
7
8
  usePresetContext,
@@ -49,6 +50,7 @@ import {
49
50
  activityUnder,
50
51
  applyQuickRange,
51
52
  assertAbsoluteDateRange,
53
+ clampCapLeft,
52
54
  clampRangeToWindow,
53
55
  defaultWindow,
54
56
  formatDurationMs,
@@ -80,6 +82,7 @@ export {
80
82
  TOUCH_HOLD_MS,
81
83
  applyQuickRange,
82
84
  assertAbsoluteDateRange,
85
+ clampCapLeft,
83
86
  getScrubberGripRenderSize,
84
87
  resolveScrubberRadius,
85
88
  } from "./geometry";
@@ -272,6 +275,8 @@ function TimeRangeScrubberControl({
272
275
  const [kbFocus, setKbFocus] = useState<string | null>(null);
273
276
  const [moreOpen, setMoreOpen] = useState(false);
274
277
  const [announce, setAnnounce] = useState("");
278
+ const [capTrackWidth, setCapTrackWidth] = useState(0);
279
+ const [capWidth, setCapWidth] = useState(0);
275
280
  const railRef = useRef<HTMLElement | null>(null);
276
281
  const dragRoleRef = useRef<DragRole | null>(null);
277
282
  const liftOriginRef = useRef<Date | null>(null);
@@ -294,7 +299,12 @@ function TimeRangeScrubberControl({
294
299
  const frameAt = latestFrameAtOrBefore(frames, scrubTime);
295
300
  const activityAt = activityUnder(activity, scrubTime);
296
301
  const fieldThemeOutline = (theme.outlineColor?.val as string | undefined) ?? "$outlineColor";
297
- const focusRing = ensureFocusVisibleRing({ outlineColor: fieldThemeOutline, outlineOffset: 2 });
302
+ // LC-71 §4 halo: circular thumb. At offset 0 the band is concentric with
303
+ // the handle's own edge and reads as a thicker handle, not a ring around it.
304
+ const focusRing = ensureFocusVisibleRing({
305
+ outlineColor: fieldThemeOutline,
306
+ outlineOffset: FOCUS_RING_HALO_OFFSET,
307
+ });
298
308
 
299
309
  const emitScrub = useCallback(
300
310
  (next: Date, nextSession: ScrubberSession) => {
@@ -490,6 +500,8 @@ function TimeRangeScrubberControl({
490
500
  };
491
501
 
492
502
  const leftPct = (time: Date) => `${timeToFraction(time, windowValue) * 100}%`;
503
+ const scrubFraction = timeToFraction(scrubTime, windowValue);
504
+ const capLeft = clampCapLeft(scrubFraction, capTrackWidth, capWidth);
493
505
  const gripFocus = kbFocus === "grip" ? focusRing : undefined;
494
506
  const startFocus = kbFocus === "brush-start" ? focusRing : undefined;
495
507
  const endFocus = kbFocus === "brush-end" ? focusRing : undefined;
@@ -551,7 +563,7 @@ function TimeRangeScrubberControl({
551
563
  data-testid="time-range-scrubber"
552
564
  opacity={disabled ? 0.5 : 1}
553
565
  >
554
- <XStack alignItems="baseline" gap="$3" justifyContent="flex-end">
566
+ <XStack alignItems="baseline" gap="$3" justifyContent="flex-end" flexWrap="wrap">
555
567
  <Text
556
568
  fontFamily="$mono"
557
569
  fontSize={12}
@@ -563,6 +575,17 @@ function TimeRangeScrubberControl({
563
575
  >
564
576
  {formatRangeReadout(value)}
565
577
  </Text>
578
+ {frameAt ? (
579
+ <Text
580
+ fontFamily="$mono"
581
+ fontSize={12}
582
+ lineHeight={22}
583
+ color="$color11"
584
+ data-part="frame-stale"
585
+ >
586
+ {`${t("frame")} −${frameStale}`}
587
+ </Text>
588
+ ) : null}
566
589
  </XStack>
567
590
  <XStack alignItems="center" gap={7}>
568
591
  <View
@@ -606,23 +629,41 @@ function TimeRangeScrubberControl({
606
629
  width="100%"
607
630
  minHeight={AXIS_BAND}
608
631
  >
609
- <XStack height={32} position="relative" marginBottom={4}>
632
+ <XStack
633
+ height={CAP_HEIGHT}
634
+ position="relative"
635
+ marginBottom={4}
636
+ width="100%"
637
+ onLayout={(event) => setCapTrackWidth(event.nativeEvent.layout.width)}
638
+ >
610
639
  <View
611
640
  position="absolute"
612
- left={leftPct(scrubTime)}
613
- x="-50%"
641
+ left={capLeft ?? `${scrubFraction * 100}%`}
642
+ x={capLeft == null ? "-50%" : 0}
614
643
  height={CAP_HEIGHT}
615
644
  paddingHorizontal={10}
645
+ flexDirection="row"
646
+ alignItems="center"
647
+ justifyContent="center"
648
+ overflow="hidden"
649
+ maxHeight={CAP_HEIGHT}
616
650
  borderRadius={CAP_RADIUS}
617
651
  borderWidth={1}
618
652
  borderColor="$borderColor"
619
653
  backgroundColor="$color2"
620
- justifyContent="center"
654
+ onLayout={(event) => setCapWidth(event.nativeEvent.layout.width)}
621
655
  data-part="cap"
622
656
  >
623
- <Text fontFamily="$mono" fontSize={12} lineHeight={22} color="$color12">
657
+ <Text
658
+ fontFamily="$mono"
659
+ fontSize={12}
660
+ lineHeight={16}
661
+ color="$color12"
662
+ numberOfLines={1}
663
+ ellipsizeMode="clip"
664
+ whiteSpace="nowrap"
665
+ >
624
666
  {formatClock(scrubTime)}
625
- {frameAt ? <Text color="$color11">{` ${t("frame")} −${frameStale}`}</Text> : null}
626
667
  </Text>
627
668
  </View>
628
669
  </XStack>
@@ -0,0 +1,12 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { osDateTimeIosDisplay } from "./osDateTimeIosDisplay";
3
+
4
+ describe("osDateTimeIosDisplay", () => {
5
+ it("uses the iOS 14+ inline calendar for dates", () => {
6
+ expect(osDateTimeIosDisplay("date")).toBe("inline");
7
+ });
8
+
9
+ it("keeps the time wheel", () => {
10
+ expect(osDateTimeIosDisplay("time")).toBe("spinner");
11
+ });
12
+ });
@@ -0,0 +1,197 @@
1
+ /**
2
+ * OS date/time sheet — iOS spinner/calendar, Android Material dialog.
3
+ *
4
+ * Catalog DatePicker/TimePicker/DatetimePicker on web keep the house calendar
5
+ * and time panel. On native those controls are the ones users expect: the
6
+ * iOS 14+ inline calendar, the time *wheel*, and Android's dialog. This
7
+ * sheet is the shared chrome so the three fields do not each invent a Modal.
8
+ *
9
+ * `native={false}` on a field skips this and keeps the catalog overlay.
10
+ */
11
+ import { useCallback, useState } from "react";
12
+ import { Modal, Pressable, StyleSheet, useColorScheme, Platform } from "react-native";
13
+ import DateTimePicker, { type DateTimePickerEvent } from "@react-native-community/datetimepicker";
14
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
15
+ import { Text, View, XStack, YStack, useTheme, useThemeName } from "tamagui";
16
+ import { formCommonColors } from "../../shared/colorRamps";
17
+ import { t } from "../../shared/t";
18
+ import { osDateTimeIosDisplay } from "./osDateTimeIosDisplay";
19
+
20
+ export type OsDateTimeMode = "date" | "time" | "datetime";
21
+
22
+ export type OsDateTimeSheetProps = {
23
+ open: boolean;
24
+ value: Date;
25
+ mode: OsDateTimeMode;
26
+ minDate?: Date;
27
+ maxDate?: Date;
28
+ onCancel: () => void;
29
+ onConfirm: (value: Date) => void;
30
+ };
31
+
32
+ function useNativePickerScheme(): "light" | "dark" {
33
+ const themeName = useThemeName();
34
+ const osScheme = useColorScheme();
35
+ if (themeName) return String(themeName).startsWith("dark") ? "dark" : "light";
36
+ return osScheme === "dark" ? "dark" : "light";
37
+ }
38
+
39
+ export function OsDateTimeSheet({
40
+ open,
41
+ value,
42
+ mode,
43
+ minDate,
44
+ maxDate,
45
+ onCancel,
46
+ onConfirm,
47
+ }: OsDateTimeSheetProps) {
48
+ const { knobProps } = useResolvedKnobs();
49
+ const themeVariant = useNativePickerScheme();
50
+ const theme = useTheme();
51
+ const accentColor = theme.accentBackground?.val;
52
+ const isIOS = Platform.OS === "ios";
53
+ const [showTimePicker, setShowTimePicker] = useState(false);
54
+ const [temp, setTemp] = useState(value);
55
+ const pickerMode: "date" | "time" =
56
+ mode === "time" || (mode === "datetime" && showTimePicker) ? "time" : "date";
57
+ const iosDisplay = osDateTimeIosDisplay(pickerMode);
58
+
59
+ const reset = useCallback(() => {
60
+ setShowTimePicker(false);
61
+ setTemp(value);
62
+ }, [value]);
63
+
64
+ const [wasOpen, setWasOpen] = useState(false);
65
+ if (open && !wasOpen) {
66
+ setWasOpen(true);
67
+ setTemp(value);
68
+ setShowTimePicker(false);
69
+ } else if (!open && wasOpen) {
70
+ setWasOpen(false);
71
+ }
72
+
73
+ function handleAndroidChange(event: DateTimePickerEvent, selected?: Date) {
74
+ if (event.type === "dismissed") {
75
+ reset();
76
+ onCancel();
77
+ return;
78
+ }
79
+ if (event.type !== "set" || !selected) return;
80
+ if (mode === "datetime" && !showTimePicker) {
81
+ setTemp(selected);
82
+ setShowTimePicker(true);
83
+ return;
84
+ }
85
+ const picked =
86
+ mode === "datetime"
87
+ ? new Date(
88
+ temp.getFullYear(),
89
+ temp.getMonth(),
90
+ temp.getDate(),
91
+ selected.getHours(),
92
+ selected.getMinutes(),
93
+ selected.getSeconds(),
94
+ )
95
+ : selected;
96
+ onConfirm(picked);
97
+ reset();
98
+ }
99
+
100
+ function handleIOSChange(_event: DateTimePickerEvent, selected?: Date) {
101
+ if (!selected) return;
102
+ if (mode === "datetime" && showTimePicker) {
103
+ setTemp(
104
+ new Date(
105
+ temp.getFullYear(),
106
+ temp.getMonth(),
107
+ temp.getDate(),
108
+ selected.getHours(),
109
+ selected.getMinutes(),
110
+ selected.getSeconds(),
111
+ ),
112
+ );
113
+ return;
114
+ }
115
+ setTemp(selected);
116
+ }
117
+
118
+ function confirm() {
119
+ onConfirm(temp);
120
+ reset();
121
+ }
122
+
123
+ if (!open) return null;
124
+
125
+ if (!isIOS) {
126
+ return (
127
+ <DateTimePicker
128
+ value={temp}
129
+ mode={pickerMode}
130
+ display="default"
131
+ onChange={handleAndroidChange}
132
+ minimumDate={mode !== "time" ? minDate : undefined}
133
+ maximumDate={mode !== "time" ? maxDate : undefined}
134
+ />
135
+ );
136
+ }
137
+
138
+ return (
139
+ <Modal transparent animationType="fade" visible={open} onRequestClose={onCancel}>
140
+ <Pressable style={styles.modalOverlay} onPress={onCancel}>
141
+ <View
142
+ pointerEvents="none"
143
+ position="absolute"
144
+ top={0}
145
+ right={0}
146
+ bottom={0}
147
+ left={0}
148
+ backgroundColor="$shadow6"
149
+ />
150
+ <Pressable onPress={(e) => e.stopPropagation()}>
151
+ <YStack
152
+ {...knobProps.elevatedSurface}
153
+ borderBottomLeftRadius={0}
154
+ borderBottomRightRadius={0}
155
+ >
156
+ <XStack justifyContent="space-between" {...knobProps.panelPadding}>
157
+ <Pressable onPress={onCancel} accessibilityRole="button">
158
+ <Text color={formCommonColors.text}>{t("Cancel")}</Text>
159
+ </Pressable>
160
+ {mode === "datetime" && !showTimePicker ? (
161
+ <Pressable onPress={() => setShowTimePicker(true)} accessibilityRole="button">
162
+ <Text color="$accentBackground">{t("Next")}</Text>
163
+ </Pressable>
164
+ ) : (
165
+ <Pressable onPress={confirm} accessibilityRole="button">
166
+ <Text color="$accentBackground">{t("Done")}</Text>
167
+ </Pressable>
168
+ )}
169
+ </XStack>
170
+ <YStack {...knobProps.panelPadding}>
171
+ <DateTimePicker
172
+ value={temp}
173
+ mode={pickerMode}
174
+ display={iosDisplay}
175
+ themeVariant={themeVariant}
176
+ accentColor={accentColor}
177
+ onChange={handleIOSChange}
178
+ minimumDate={mode !== "time" ? minDate : undefined}
179
+ maximumDate={mode !== "time" ? maxDate : undefined}
180
+ style={pickerMode === "time" ? styles.wheel : styles.calendar}
181
+ />
182
+ </YStack>
183
+ </YStack>
184
+ </Pressable>
185
+ </Pressable>
186
+ </Modal>
187
+ );
188
+ }
189
+
190
+ const styles = StyleSheet.create({
191
+ modalOverlay: {
192
+ flex: 1,
193
+ justifyContent: "flex-end",
194
+ },
195
+ wheel: { height: 216 },
196
+ calendar: { height: 360 },
197
+ });
@@ -0,0 +1,4 @@
1
+ /** iOS date is the inline calendar; time keeps the wheel users expect. */
2
+ export function osDateTimeIosDisplay(mode: "date" | "time"): "inline" | "spinner" {
3
+ return mode === "time" ? "spinner" : "inline";
4
+ }