@multiplatform.one/forms 7.77.1 → 7.78.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 (296) hide show
  1. package/dist/cjs/Button/IconButtonGroup.cjs +58 -27
  2. package/dist/cjs/Button/IconButtonGroup.native.js +64 -29
  3. package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -1
  4. package/dist/cjs/Button/circleGlyph.cjs +12 -2
  5. package/dist/cjs/Button/circleGlyph.native.js +12 -1
  6. package/dist/cjs/Button/circleGlyph.native.js.map +1 -1
  7. package/dist/cjs/Button/glass.cjs +2 -1
  8. package/dist/cjs/Button/glass.native.js +44 -19
  9. package/dist/cjs/Button/glass.native.js.map +1 -1
  10. package/dist/cjs/Button/iconButtonGroupContext.cjs +7 -1
  11. package/dist/cjs/Button/iconButtonGroupContext.native.js +7 -1
  12. package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -1
  13. package/dist/cjs/Button/index.cjs +15 -23
  14. package/dist/cjs/Button/index.native.js +15 -24
  15. package/dist/cjs/Button/index.native.js.map +1 -1
  16. package/dist/cjs/Button/webGlass.cjs +9 -5
  17. package/dist/cjs/Button/webGlass.native.js +9 -5
  18. package/dist/cjs/Button/webGlass.native.js.map +1 -1
  19. package/dist/cjs/FloatingPanel/coverRect.cjs +50 -0
  20. package/dist/cjs/FloatingPanel/coverRect.native.js +54 -0
  21. package/dist/cjs/FloatingPanel/coverRect.native.js.map +1 -0
  22. package/dist/cjs/FloatingPanel/iconEcho.cjs +3 -1
  23. package/dist/cjs/FloatingPanel/iconEcho.native.js +3 -1
  24. package/dist/cjs/FloatingPanel/iconEcho.native.js.map +1 -1
  25. package/dist/cjs/FloatingPanel/index.cjs +23 -13
  26. package/dist/cjs/FloatingPanel/index.native.js +41 -15
  27. package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
  28. package/dist/cjs/InputParts/index.cjs +5 -0
  29. package/dist/cjs/InputParts/index.native.js +7 -0
  30. package/dist/cjs/InputParts/index.native.js.map +1 -1
  31. package/dist/cjs/{Button/mediaPlate.cjs → InputParts/keyboardAppearance.cjs} +7 -8
  32. package/dist/{jsx/Button/mediaPlate.native.js → cjs/InputParts/keyboardAppearance.native.js} +8 -9
  33. package/dist/cjs/InputParts/keyboardAppearance.native.js.map +1 -0
  34. package/dist/cjs/{Button/mediaPlate.native.js → fields/Duration/DurationWheel.cjs} +11 -11
  35. package/dist/cjs/fields/Duration/DurationWheel.native.js +132 -0
  36. package/dist/cjs/fields/Duration/DurationWheel.native.js.map +1 -0
  37. package/dist/cjs/fields/Duration/durationMath.cjs +19 -1
  38. package/dist/cjs/fields/Duration/durationMath.native.js +19 -1
  39. package/dist/cjs/fields/Duration/durationMath.native.js.map +1 -1
  40. package/dist/cjs/fields/Duration/durationWheelTypes.cjs +18 -0
  41. package/dist/cjs/fields/Duration/durationWheelTypes.native.js +21 -0
  42. package/dist/cjs/fields/Duration/durationWheelTypes.native.js.map +1 -0
  43. package/dist/cjs/fields/Duration/index.cjs +30 -10
  44. package/dist/cjs/fields/Duration/index.native.js +32 -12
  45. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  46. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +8 -7
  47. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  48. package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  49. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.cjs +1 -1
  50. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  51. package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  52. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.cjs +1 -1
  53. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  54. package/dist/cjs/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  55. package/dist/cjs/index.cjs +0 -1
  56. package/dist/cjs/index.native.js +0 -1
  57. package/dist/cjs/index.native.js.map +1 -1
  58. package/dist/esm/Button/IconButtonGroup.mjs +65 -33
  59. package/dist/esm/Button/IconButtonGroup.mjs.map +1 -1
  60. package/dist/esm/Button/IconButtonGroup.native.js +71 -35
  61. package/dist/esm/Button/IconButtonGroup.native.js.map +1 -1
  62. package/dist/esm/Button/circleGlyph.mjs +9 -1
  63. package/dist/esm/Button/circleGlyph.mjs.map +1 -1
  64. package/dist/esm/Button/circleGlyph.native.js +10 -1
  65. package/dist/esm/Button/circleGlyph.native.js.map +1 -1
  66. package/dist/esm/Button/glass.mjs +2 -1
  67. package/dist/esm/Button/glass.mjs.map +1 -1
  68. package/dist/esm/Button/glass.native.js +43 -19
  69. package/dist/esm/Button/glass.native.js.map +1 -1
  70. package/dist/esm/Button/iconButtonGroupContext.mjs +5 -1
  71. package/dist/esm/Button/iconButtonGroupContext.mjs.map +1 -1
  72. package/dist/esm/Button/iconButtonGroupContext.native.js +5 -1
  73. package/dist/esm/Button/iconButtonGroupContext.native.js.map +1 -1
  74. package/dist/esm/Button/index.mjs +15 -22
  75. package/dist/esm/Button/index.mjs.map +1 -1
  76. package/dist/esm/Button/index.native.js +15 -23
  77. package/dist/esm/Button/index.native.js.map +1 -1
  78. package/dist/esm/Button/webGlass.mjs +10 -6
  79. package/dist/esm/Button/webGlass.mjs.map +1 -1
  80. package/dist/esm/Button/webGlass.native.js +10 -6
  81. package/dist/esm/Button/webGlass.native.js.map +1 -1
  82. package/dist/esm/FloatingPanel/coverRect.mjs +23 -0
  83. package/dist/esm/FloatingPanel/coverRect.mjs.map +1 -0
  84. package/dist/esm/FloatingPanel/coverRect.native.js +24 -0
  85. package/dist/esm/FloatingPanel/coverRect.native.js.map +1 -0
  86. package/dist/esm/FloatingPanel/iconEcho.mjs +4 -2
  87. package/dist/esm/FloatingPanel/iconEcho.mjs.map +1 -1
  88. package/dist/esm/FloatingPanel/iconEcho.native.js +4 -2
  89. package/dist/esm/FloatingPanel/iconEcho.native.js.map +1 -1
  90. package/dist/esm/FloatingPanel/index.mjs +23 -13
  91. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  92. package/dist/esm/FloatingPanel/index.native.js +41 -15
  93. package/dist/esm/FloatingPanel/index.native.js.map +1 -1
  94. package/dist/esm/InputParts/index.mjs +6 -1
  95. package/dist/esm/InputParts/index.mjs.map +1 -1
  96. package/dist/esm/InputParts/index.native.js +8 -1
  97. package/dist/esm/InputParts/index.native.js.map +1 -1
  98. package/dist/esm/InputParts/keyboardAppearance.mjs +5 -0
  99. package/dist/esm/InputParts/keyboardAppearance.mjs.map +1 -0
  100. package/dist/esm/InputParts/keyboardAppearance.native.js +5 -0
  101. package/dist/esm/InputParts/keyboardAppearance.native.js.map +1 -0
  102. package/dist/esm/fields/Duration/DurationWheel.mjs +8 -0
  103. package/dist/esm/fields/Duration/DurationWheel.mjs.map +1 -0
  104. package/dist/esm/fields/Duration/DurationWheel.native.js +103 -0
  105. package/dist/esm/fields/Duration/DurationWheel.native.js.map +1 -0
  106. package/dist/esm/fields/Duration/durationMath.mjs +16 -1
  107. package/dist/esm/fields/Duration/durationMath.mjs.map +1 -1
  108. package/dist/esm/fields/Duration/durationMath.native.js +16 -1
  109. package/dist/esm/fields/Duration/durationMath.native.js.map +1 -1
  110. package/dist/esm/fields/Duration/durationWheelTypes.mjs +2 -0
  111. package/dist/esm/fields/Duration/durationWheelTypes.mjs.map +1 -0
  112. package/dist/esm/fields/Duration/durationWheelTypes.native.js +2 -0
  113. package/dist/esm/fields/Duration/durationWheelTypes.native.js.map +1 -0
  114. package/dist/esm/fields/Duration/index.mjs +28 -11
  115. package/dist/esm/fields/Duration/index.mjs.map +1 -1
  116. package/dist/esm/fields/Duration/index.native.js +30 -13
  117. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  118. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
  119. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  120. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  121. package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  122. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
  123. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
  124. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  125. package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -1
  126. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
  127. package/dist/esm/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
  128. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  129. package/dist/esm/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  130. package/dist/esm/index.mjs +2 -2
  131. package/dist/esm/index.mjs.map +1 -1
  132. package/dist/esm/index.native.js +2 -2
  133. package/dist/esm/index.native.js.map +1 -1
  134. package/dist/jsx/Button/IconButtonGroup.mjs +65 -33
  135. package/dist/jsx/Button/IconButtonGroup.mjs.map +1 -1
  136. package/dist/jsx/Button/IconButtonGroup.native.js +64 -29
  137. package/dist/jsx/Button/IconButtonGroup.native.js.map +1 -1
  138. package/dist/jsx/Button/circleGlyph.mjs +9 -1
  139. package/dist/jsx/Button/circleGlyph.mjs.map +1 -1
  140. package/dist/jsx/Button/circleGlyph.native.js +12 -1
  141. package/dist/jsx/Button/circleGlyph.native.js.map +1 -1
  142. package/dist/jsx/Button/glass.mjs +2 -1
  143. package/dist/jsx/Button/glass.mjs.map +1 -1
  144. package/dist/jsx/Button/glass.native.js +44 -19
  145. package/dist/jsx/Button/glass.native.js.map +1 -1
  146. package/dist/jsx/Button/iconButtonGroupContext.mjs +5 -1
  147. package/dist/jsx/Button/iconButtonGroupContext.mjs.map +1 -1
  148. package/dist/jsx/Button/iconButtonGroupContext.native.js +7 -1
  149. package/dist/jsx/Button/iconButtonGroupContext.native.js.map +1 -1
  150. package/dist/jsx/Button/index.mjs +15 -22
  151. package/dist/jsx/Button/index.mjs.map +1 -1
  152. package/dist/jsx/Button/index.native.js +15 -24
  153. package/dist/jsx/Button/index.native.js.map +1 -1
  154. package/dist/jsx/Button/webGlass.mjs +10 -6
  155. package/dist/jsx/Button/webGlass.mjs.map +1 -1
  156. package/dist/jsx/Button/webGlass.native.js +9 -5
  157. package/dist/jsx/Button/webGlass.native.js.map +1 -1
  158. package/dist/jsx/FloatingPanel/coverRect.mjs +23 -0
  159. package/dist/jsx/FloatingPanel/coverRect.mjs.map +1 -0
  160. package/dist/jsx/FloatingPanel/coverRect.native.js +54 -0
  161. package/dist/jsx/FloatingPanel/coverRect.native.js.map +1 -0
  162. package/dist/jsx/FloatingPanel/iconEcho.mjs +4 -2
  163. package/dist/jsx/FloatingPanel/iconEcho.mjs.map +1 -1
  164. package/dist/jsx/FloatingPanel/iconEcho.native.js +3 -1
  165. package/dist/jsx/FloatingPanel/iconEcho.native.js.map +1 -1
  166. package/dist/jsx/FloatingPanel/index.mjs +23 -13
  167. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  168. package/dist/jsx/FloatingPanel/index.native.js +41 -15
  169. package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
  170. package/dist/jsx/InputParts/index.mjs +6 -1
  171. package/dist/jsx/InputParts/index.mjs.map +1 -1
  172. package/dist/jsx/InputParts/index.native.js +7 -0
  173. package/dist/jsx/InputParts/index.native.js.map +1 -1
  174. package/dist/jsx/InputParts/keyboardAppearance.mjs +5 -0
  175. package/dist/jsx/InputParts/keyboardAppearance.mjs.map +1 -0
  176. package/dist/jsx/InputParts/keyboardAppearance.native.js +33 -0
  177. package/dist/jsx/InputParts/keyboardAppearance.native.js.map +1 -0
  178. package/dist/jsx/fields/Duration/DurationWheel.mjs +8 -0
  179. package/dist/jsx/fields/Duration/DurationWheel.mjs.map +1 -0
  180. package/dist/jsx/fields/Duration/DurationWheel.native.js +132 -0
  181. package/dist/jsx/fields/Duration/DurationWheel.native.js.map +1 -0
  182. package/dist/jsx/fields/Duration/durationMath.mjs +16 -1
  183. package/dist/jsx/fields/Duration/durationMath.mjs.map +1 -1
  184. package/dist/jsx/fields/Duration/durationMath.native.js +19 -1
  185. package/dist/jsx/fields/Duration/durationMath.native.js.map +1 -1
  186. package/dist/jsx/fields/Duration/durationWheelTypes.mjs +2 -0
  187. package/dist/jsx/fields/Duration/durationWheelTypes.mjs.map +1 -0
  188. package/dist/jsx/fields/Duration/durationWheelTypes.native.js +21 -0
  189. package/dist/jsx/fields/Duration/durationWheelTypes.native.js.map +1 -0
  190. package/dist/jsx/fields/Duration/index.mjs +28 -11
  191. package/dist/jsx/fields/Duration/index.mjs.map +1 -1
  192. package/dist/jsx/fields/Duration/index.native.js +32 -12
  193. package/dist/jsx/fields/Duration/index.native.js.map +1 -1
  194. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +8 -7
  195. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
  196. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +8 -7
  197. package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
  198. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs +1 -1
  199. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -1
  200. package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js +1 -1
  201. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs +1 -1
  202. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.mjs.map +1 -1
  203. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js +1 -1
  204. package/dist/jsx/fields/nativeDateTime/sheetKeyboard.native.js.map +1 -1
  205. package/dist/jsx/index.js +2 -2
  206. package/dist/jsx/index.js.map +1 -1
  207. package/dist/jsx/index.mjs +2 -2
  208. package/dist/jsx/index.mjs.map +1 -1
  209. package/dist/jsx/index.native.js +0 -1
  210. package/dist/jsx/index.native.js.map +1 -1
  211. package/package.json +8 -8
  212. package/src/Button/Button.iconButton.spec.tsx +47 -7
  213. package/src/Button/IconButtonGroup.spec.tsx +103 -19
  214. package/src/Button/IconButtonGroup.tsx +84 -46
  215. package/src/Button/circleGlyph.ts +18 -0
  216. package/src/Button/glass.native.spec.ts +62 -0
  217. package/src/Button/glass.native.ts +58 -37
  218. package/src/Button/glass.spec.ts +64 -16
  219. package/src/Button/glass.ts +16 -12
  220. package/src/Button/iconButtonGroupContext.ts +27 -3
  221. package/src/Button/index.tsx +21 -33
  222. package/src/Button/webGlass.ts +25 -18
  223. package/src/FloatingPanel/FloatingPanel.iconEcho.spec.tsx +61 -1
  224. package/src/FloatingPanel/FloatingPanel.menuFace.native.spec.tsx +104 -0
  225. package/src/FloatingPanel/coverRect.ts +42 -0
  226. package/src/FloatingPanel/iconEcho.tsx +9 -3
  227. package/src/FloatingPanel/index.native.tsx +63 -18
  228. package/src/FloatingPanel/index.tsx +35 -15
  229. package/src/InputParts/index.tsx +6 -0
  230. package/src/InputParts/keyboardAppearance.spec.ts +12 -0
  231. package/src/InputParts/keyboardAppearance.ts +4 -0
  232. package/src/fields/Duration/Duration.spec.tsx +19 -10
  233. package/src/fields/Duration/Duration.wheel.native.spec.tsx +98 -0
  234. package/src/fields/Duration/DurationWheel.native.tsx +101 -0
  235. package/src/fields/Duration/DurationWheel.tsx +11 -0
  236. package/src/fields/Duration/durationMath.spec.ts +43 -0
  237. package/src/fields/Duration/durationMath.ts +31 -0
  238. package/src/fields/Duration/durationWheelTypes.ts +18 -0
  239. package/src/fields/Duration/index.tsx +58 -27
  240. package/src/fields/nativeDateTime/OsDateTimeSheet.countdown.native.spec.tsx +121 -0
  241. package/src/fields/nativeDateTime/OsDateTimeSheet.spec.ts +4 -0
  242. package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +14 -9
  243. package/src/fields/nativeDateTime/osDateTimeIosDisplay.ts +3 -3
  244. package/src/fields/nativeDateTime/sheetKeyboard.spec.ts +6 -0
  245. package/src/fields/nativeDateTime/sheetKeyboard.ts +2 -2
  246. package/src/index.ts +0 -1
  247. package/types/Button/IconButtonGroup.d.ts +7 -12
  248. package/types/Button/IconButtonGroup.d.ts.map +1 -1
  249. package/types/Button/circleGlyph.d.ts +12 -0
  250. package/types/Button/circleGlyph.d.ts.map +1 -1
  251. package/types/Button/glass.d.ts +7 -11
  252. package/types/Button/glass.d.ts.map +1 -1
  253. package/types/Button/glass.native.d.ts +22 -10
  254. package/types/Button/glass.native.d.ts.map +1 -1
  255. package/types/Button/iconButtonGroupContext.d.ts +21 -3
  256. package/types/Button/iconButtonGroupContext.d.ts.map +1 -1
  257. package/types/Button/index.d.ts +3 -10
  258. package/types/Button/index.d.ts.map +1 -1
  259. package/types/Button/webGlass.d.ts +8 -16
  260. package/types/Button/webGlass.d.ts.map +1 -1
  261. package/types/FloatingPanel/coverRect.d.ts +25 -0
  262. package/types/FloatingPanel/coverRect.d.ts.map +1 -0
  263. package/types/FloatingPanel/index.d.ts.map +1 -1
  264. package/types/FloatingPanel/index.native.d.ts.map +1 -1
  265. package/types/InputParts/index.d.ts.map +1 -1
  266. package/types/InputParts/keyboardAppearance.d.ts +3 -0
  267. package/types/InputParts/keyboardAppearance.d.ts.map +1 -0
  268. package/types/fields/Duration/DurationWheel.d.ts +5 -0
  269. package/types/fields/Duration/DurationWheel.d.ts.map +1 -0
  270. package/types/fields/Duration/DurationWheel.native.d.ts +5 -0
  271. package/types/fields/Duration/DurationWheel.native.d.ts.map +1 -0
  272. package/types/fields/Duration/durationMath.d.ts +13 -0
  273. package/types/fields/Duration/durationMath.d.ts.map +1 -1
  274. package/types/fields/Duration/durationWheelTypes.d.ts +18 -0
  275. package/types/fields/Duration/durationWheelTypes.d.ts.map +1 -0
  276. package/types/fields/Duration/index.d.ts +8 -3
  277. package/types/fields/Duration/index.d.ts.map +1 -1
  278. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts +1 -1
  279. package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
  280. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts +2 -2
  281. package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts.map +1 -1
  282. package/types/fields/nativeDateTime/sheetKeyboard.d.ts +1 -1
  283. package/types/fields/nativeDateTime/sheetKeyboard.d.ts.map +1 -1
  284. package/types/index.d.ts +1 -1
  285. package/types/index.d.ts.map +1 -1
  286. package/dist/cjs/Button/mediaPlate.native.js.map +0 -1
  287. package/dist/esm/Button/mediaPlate.mjs +0 -6
  288. package/dist/esm/Button/mediaPlate.mjs.map +0 -1
  289. package/dist/esm/Button/mediaPlate.native.js +0 -6
  290. package/dist/esm/Button/mediaPlate.native.js.map +0 -1
  291. package/dist/jsx/Button/mediaPlate.mjs +0 -6
  292. package/dist/jsx/Button/mediaPlate.mjs.map +0 -1
  293. package/dist/jsx/Button/mediaPlate.native.js.map +0 -1
  294. package/src/Button/mediaPlate.ts +0 -9
  295. package/types/Button/mediaPlate.d.ts +0 -10
  296. package/types/Button/mediaPlate.d.ts.map +0 -1
@@ -2,10 +2,13 @@ import { describe, expect, it } from "vitest";
2
2
  import { sizeRecipeForToken } from "@multiplatform.one/theme";
3
3
  import {
4
4
  durationSegmentWidth,
5
+ durationToWheelDate,
6
+ formatDurationParts,
5
7
  normalizeSeconds,
6
8
  parseDurationText,
7
9
  partsToSeconds,
8
10
  secondsToParts,
11
+ wheelDateToDuration,
9
12
  } from "./durationMath";
10
13
 
11
14
  describe("durationMath", () => {
@@ -64,4 +67,44 @@ describe("durationMath", () => {
64
67
  expect(durationSegmentWidth("$2", 2)).not.toBe(durationSegmentWidth("$6", 2));
65
68
  expect(durationSegmentWidth("$2", 2)).toBeLessThan(durationSegmentWidth("$4", 2));
66
69
  });
70
+
71
+ describe("countdown wheel (FZ-8111)", () => {
72
+ const at = (h: number, m: number) => {
73
+ const d = new Date(2026, 9, 29);
74
+ d.setHours(h, m, 0, 0);
75
+ return d;
76
+ };
77
+
78
+ it("reads hours and minutes within the day onto the wheel", () => {
79
+ const date = durationToWheelDate(3720);
80
+ expect([date.getHours(), date.getMinutes(), date.getSeconds()]).toEqual([1, 2, 0]);
81
+ const late = durationToWheelDate(23 * 3600 + 59 * 60);
82
+ expect([late.getHours(), late.getMinutes()]).toEqual([23, 59]);
83
+ });
84
+
85
+ it("round-trips every wheel position back to the same seconds", () => {
86
+ for (const seconds of [0, 60, 900, 3720, 9000, 86340]) {
87
+ expect(wheelDateToDuration(durationToWheelDate(seconds), 0)).toBe(seconds);
88
+ }
89
+ });
90
+
91
+ it("sets seconds from a wheel pick", () => {
92
+ expect(wheelDateToDuration(at(2, 30), 0)).toBe(9000);
93
+ expect(wheelDateToDuration(at(0, 15), 3720)).toBe(900);
94
+ });
95
+
96
+ it("carries whole days and stray seconds the wheel cannot show", () => {
97
+ expect(wheelDateToDuration(at(2, 30), 86400 + 3600 + 7)).toBe(86400 + 9000 + 7);
98
+ const wheel = durationToWheelDate(90000);
99
+ expect([wheel.getHours(), wheel.getMinutes()]).toEqual([1, 0]);
100
+ });
101
+
102
+ it("formats the trigger text with or without days", () => {
103
+ const units = { d: "d", h: "h", m: "m" };
104
+ expect(formatDurationParts(secondsToParts(3720, true), true, units)).toBe("1 h 2 m");
105
+ expect(formatDurationParts(secondsToParts(90000, true), true, units)).toBe("25 h 0 m");
106
+ expect(formatDurationParts(secondsToParts(93784, false), false, units)).toBe("1 d 2 h 3 m");
107
+ expect(formatDurationParts(secondsToParts(3720, false), false, units)).toBe("1 h 2 m");
108
+ });
109
+ });
67
110
  });
@@ -76,3 +76,34 @@ export function durationSegmentWidth(sizeToken: string, digits: number): number
76
76
  const recipe = sizeRecipeForToken(sizeToken);
77
77
  return recipe.fontSize * digits;
78
78
  }
79
+
80
+ const DAY = 86400;
81
+
82
+ /** The countdown wheel's reading of a duration: hours and minutes within one day, on today's date. */
83
+ export function durationToWheelDate(total: number): Date {
84
+ const s = normalizeSeconds(total) % DAY;
85
+ const date = new Date();
86
+ date.setHours(Math.floor(s / 3600), Math.floor((s % 3600) / 60), 0, 0);
87
+ return date;
88
+ }
89
+
90
+ /**
91
+ * A wheel pick back to seconds. The wheel spans 0 h 0 m to 23 h 59 m, so the
92
+ * whole days and the stray seconds of the previous value carry over untouched.
93
+ */
94
+ export function wheelDateToDuration(picked: Date, previous: number): number {
95
+ const prev = normalizeSeconds(previous);
96
+ return (
97
+ Math.floor(prev / DAY) * DAY + picked.getHours() * 3600 + picked.getMinutes() * 60 + (prev % 60)
98
+ );
99
+ }
100
+
101
+ /** "1 h 2 m", "1 d 2 h 3 m": the wheel trigger's text, units from the caller. */
102
+ export function formatDurationParts(
103
+ parts: DurationParts,
104
+ hideDays: boolean,
105
+ units: { d: string; h: string; m: string },
106
+ ): string {
107
+ const head = hideDays || parts.days === 0 ? [] : [`${parts.days} ${units.d}`];
108
+ return [...head, `${parts.hours} ${units.h}`, `${parts.minutes} ${units.m}`].join(" ");
109
+ }
@@ -0,0 +1,18 @@
1
+ import type { KnobProps } from "@multiplatform.one/theme";
2
+ import type { ReactNode } from "react";
3
+ import type { SizeTokens } from "tamagui";
4
+
5
+ export interface DurationWheelFieldProps {
6
+ seconds: number;
7
+ onChange: (seconds: number) => void;
8
+ disabled?: boolean;
9
+ readOnly?: boolean;
10
+ foldDays: boolean;
11
+ clockIcon?: ReactNode | null;
12
+ hasError?: boolean;
13
+ id?: string;
14
+ hasLabel?: boolean;
15
+ required?: boolean;
16
+ size?: SizeTokens;
17
+ knobProps: KnobProps;
18
+ }
@@ -5,7 +5,7 @@ import type { ReactNode } from "react";
5
5
  import { useCallback, useState } from "react";
6
6
  import type { LabelProps, SizeTokens } from "tamagui";
7
7
  import { Text, XStack, YStack, isWeb } from "tamagui";
8
- import { Button } from "../../Button";
8
+ import { Chip } from "../../Chip";
9
9
  import { Skeleton } from "../../Skeleton";
10
10
  import { Field, FieldLayout } from "../../fieldLayout";
11
11
  import { Input as InputParts } from "../../InputParts";
@@ -20,22 +20,29 @@ import {
20
20
  useResolvedValidators,
21
21
  } from "../../shared/utils";
22
22
  import type { AnyFormApi } from "../../types";
23
+ import { DurationWheelField, durationWheelAvailable } from "./DurationWheel";
23
24
  import {
24
25
  durationSegmentWidth,
26
+ durationToWheelDate,
27
+ formatDurationParts,
25
28
  normalizeSeconds,
26
29
  parseDurationText,
27
30
  partsToSeconds,
28
31
  secondsToParts,
32
+ wheelDateToDuration,
29
33
  type DurationParts,
30
34
  } from "./durationMath";
31
35
 
32
36
  export type { DurationParts };
33
37
  export {
34
38
  durationSegmentWidth,
39
+ durationToWheelDate,
40
+ formatDurationParts,
35
41
  normalizeSeconds,
36
42
  parseDurationText,
37
43
  partsToSeconds,
38
44
  secondsToParts,
45
+ wheelDateToDuration,
39
46
  };
40
47
 
41
48
  export interface DurationPreset {
@@ -374,16 +381,15 @@ function DurationPresets({
374
381
  {presets.map((preset) => {
375
382
  const selected = seconds === preset.seconds;
376
383
  return (
377
- <Button
378
- key={preset.seconds}
379
- size={DURATION_PRESET_SIZE}
380
- outlined={!selected}
381
- accent={selected}
382
- data-duration-preset={preset.label}
383
- onPress={() => onChange(preset.seconds)}
384
- >
385
- {preset.label}
386
- </Button>
384
+ <XStack key={preset.seconds} data-duration-preset={preset.label}>
385
+ <Chip
386
+ size={DURATION_PRESET_SIZE}
387
+ selected={selected}
388
+ onPress={() => onChange(preset.seconds)}
389
+ >
390
+ {preset.label}
391
+ </Chip>
392
+ </XStack>
387
393
  );
388
394
  })}
389
395
  </XStack>
@@ -425,6 +431,11 @@ export interface DurationProps {
425
431
  clockIcon?: ReactNode | null;
426
432
  /** When true, renders a skeleton placeholder instead of the duration picker */
427
433
  skeleton?: boolean;
434
+ /**
435
+ * On iOS and Android a tap opens the system hours/minutes wheel (LC-266).
436
+ * `false` keeps the typed segments there too. Web always types.
437
+ */
438
+ native?: boolean;
428
439
  }
429
440
 
430
441
  export function Duration({
@@ -450,6 +461,7 @@ export function Duration({
450
461
  placeholder = t("Select duration"),
451
462
  clockIcon,
452
463
  skeleton,
464
+ native,
453
465
  }: DurationProps) {
454
466
  const hydrationTouch = useTouchSurface();
455
467
  const { resolvedForm, knobProps, id } = useFormField({
@@ -489,24 +501,43 @@ export function Duration({
489
501
  );
490
502
  }
491
503
 
504
+ const wheel = native !== false && !showSeconds && durationWheelAvailable();
505
+
492
506
  const fieldBody = (current: number, onValueChange: (s: number) => void, hasError?: boolean) => (
493
507
  <YStack {...knobProps.gap}>
494
- <DurationControl
495
- seconds={current}
496
- onChange={onValueChange}
497
- disabled={disabled}
498
- readOnly={readOnly}
499
- hideDays={hideDays}
500
- showSeconds={showSeconds}
501
- clockIcon={clockIcon}
502
- hasError={hasError}
503
- id={id}
504
- hasLabel={Boolean(label)}
505
- placeholder={placeholder}
506
- required={required}
507
- size={size}
508
- knobProps={knobProps}
509
- />
508
+ {wheel ? (
509
+ <DurationWheelField
510
+ seconds={current}
511
+ onChange={onValueChange}
512
+ disabled={disabled}
513
+ readOnly={readOnly}
514
+ foldDays={hideDays}
515
+ clockIcon={clockIcon}
516
+ hasError={hasError}
517
+ id={id}
518
+ hasLabel={Boolean(label)}
519
+ required={required}
520
+ size={size}
521
+ knobProps={knobProps}
522
+ />
523
+ ) : (
524
+ <DurationControl
525
+ seconds={current}
526
+ onChange={onValueChange}
527
+ disabled={disabled}
528
+ readOnly={readOnly}
529
+ hideDays={hideDays}
530
+ showSeconds={showSeconds}
531
+ clockIcon={clockIcon}
532
+ hasError={hasError}
533
+ id={id}
534
+ hasLabel={Boolean(label)}
535
+ placeholder={placeholder}
536
+ required={required}
537
+ size={size}
538
+ knobProps={knobProps}
539
+ />
540
+ )}
510
541
  {readOnly || disabled || inTableCell ? null : (
511
542
  <DurationPresets
512
543
  seconds={current}
@@ -0,0 +1,121 @@
1
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
2
+ import { fireEvent } from "@testing-library/react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { OsDateTimeSheet } from "./OsDateTimeSheet";
5
+ import { DATE_PICKER_WHEEL_HEIGHT } from "./sheetKeyboard";
6
+
7
+ const os = vi.hoisted(() => ({ name: "ios" as "ios" | "android" }));
8
+ const seen = vi.hoisted(() => ({ props: [] as Record<string, unknown>[] }));
9
+
10
+ vi.mock("react-native-web", async (importOriginal) => ({
11
+ ...(await importOriginal<typeof import("react-native")>()),
12
+ useWindowDimensions: () => ({ width: 390, height: 844, scale: 3, fontScale: 1 }),
13
+ }));
14
+
15
+ vi.mock("@multiplatform.one/platform", async (importOriginal) => {
16
+ const actual = (await importOriginal()) as Record<string, unknown>;
17
+ const real = actual.platform as Record<string, unknown>;
18
+ return {
19
+ ...actual,
20
+ platform: new Proxy(
21
+ {},
22
+ {
23
+ get(_target, prop) {
24
+ if (prop === "isIos") return os.name === "ios";
25
+ if (prop === "isAndroid") return os.name === "android";
26
+ return real[prop as string];
27
+ },
28
+ },
29
+ ),
30
+ };
31
+ });
32
+
33
+ vi.mock("./dateTimePickerLoader", () => ({
34
+ loadDateTimePicker: () => (props: Record<string, unknown>) => {
35
+ seen.props.push(props);
36
+ return <div data-testid="os-picker" />;
37
+ },
38
+ }));
39
+
40
+ const at = (h: number, m: number) => {
41
+ const d = new Date(2026, 9, 29);
42
+ d.setHours(h, m, 0, 0);
43
+ return d;
44
+ };
45
+
46
+ describe("OsDateTimeSheet countdown (FZ-8111)", () => {
47
+ it("opens the iOS countdown-timer wheel in the app's scheme", () => {
48
+ os.name = "ios";
49
+ seen.props = [];
50
+ renderWithProviders(
51
+ <OsDateTimeSheet
52
+ open
53
+ mode="countdown"
54
+ value={at(1, 2)}
55
+ onCancel={vi.fn()}
56
+ onConfirm={vi.fn()}
57
+ />,
58
+ );
59
+ const props = seen.props.at(-1)!;
60
+ expect(props.mode).toBe("countdown");
61
+ expect(props.display).toBe("spinner");
62
+ expect(props.themeVariant).toBe("light");
63
+ expect((props.value as Date).getHours()).toBe(1);
64
+ expect((props.value as Date).getMinutes()).toBe(2);
65
+ expect((props.style as { height: number }).height).toBe(DATE_PICKER_WHEEL_HEIGHT);
66
+ });
67
+
68
+ it("confirms the wheel's hours and minutes", () => {
69
+ os.name = "ios";
70
+ seen.props = [];
71
+ const onConfirm = vi.fn();
72
+ const onCancel = vi.fn();
73
+ const result = renderWithProviders(
74
+ <OsDateTimeSheet
75
+ open
76
+ mode="countdown"
77
+ value={at(1, 2)}
78
+ onCancel={onCancel}
79
+ onConfirm={onConfirm}
80
+ />,
81
+ );
82
+ fireEvent.click(result.getByRole("button", { name: "Done" }));
83
+ expect(onConfirm).toHaveBeenCalledOnce();
84
+ const picked = onConfirm.mock.calls[0]![0] as Date;
85
+ expect([picked.getHours(), picked.getMinutes()]).toEqual([1, 2]);
86
+ fireEvent.click(result.getByRole("button", { name: "Cancel" }));
87
+ expect(onCancel).toHaveBeenCalledOnce();
88
+ });
89
+
90
+ it("opens Android's 24-hour spinner, never the AM/PM clock dial", () => {
91
+ os.name = "android";
92
+ seen.props = [];
93
+ renderWithProviders(
94
+ <OsDateTimeSheet
95
+ open
96
+ mode="countdown"
97
+ value={at(2, 30)}
98
+ onCancel={vi.fn()}
99
+ onConfirm={vi.fn()}
100
+ />,
101
+ );
102
+ const props = seen.props.at(-1)!;
103
+ expect(props.mode).toBe("time");
104
+ expect(props.display).toBe("spinner");
105
+ expect(props.is24Hour).toBe(true);
106
+ os.name = "ios";
107
+ });
108
+
109
+ it("leaves the Android date dialog as it was", () => {
110
+ os.name = "android";
111
+ seen.props = [];
112
+ renderWithProviders(
113
+ <OsDateTimeSheet open mode="date" value={at(0, 0)} onCancel={vi.fn()} onConfirm={vi.fn()} />,
114
+ );
115
+ const props = seen.props.at(-1)!;
116
+ expect(props.mode).toBe("date");
117
+ expect(props.display).toBe("default");
118
+ expect(props.is24Hour).toBeUndefined();
119
+ os.name = "ios";
120
+ });
121
+ });
@@ -9,4 +9,8 @@ describe("osDateTimeIosDisplay", () => {
9
9
  it("keeps the time wheel", () => {
10
10
  expect(osDateTimeIosDisplay("time")).toBe("spinner");
11
11
  });
12
+
13
+ it("keeps the wheel for a countdown (FZ-8111)", () => {
14
+ expect(osDateTimeIosDisplay("countdown")).toBe("spinner");
15
+ });
12
16
  });
@@ -33,7 +33,7 @@ import { osDateTimeIosDisplay } from "./osDateTimeIosDisplay";
33
33
  import { datePickerHeight, dateSheetBottomPadding, dateSheetMaxHeight } from "./sheetKeyboard";
34
34
  import { useKeyboardInset } from "../../shared/useKeyboardInset";
35
35
 
36
- export type OsDateTimeMode = "date" | "time" | "datetime";
36
+ export type OsDateTimeMode = "date" | "time" | "datetime" | "countdown";
37
37
 
38
38
  export type OsDateTimeSheetProps = {
39
39
  open: boolean;
@@ -74,8 +74,12 @@ export function OsDateTimeSheet({
74
74
  const maxHeight = dateSheetMaxHeight(windowHeight, keyboardInset, topInset);
75
75
  const [showTimePicker, setShowTimePicker] = useState(false);
76
76
  const [temp, setTemp] = useState(value);
77
- const pickerMode: "date" | "time" =
78
- mode === "time" || (mode === "datetime" && showTimePicker) ? "time" : "date";
77
+ const pickerMode: "date" | "time" | "countdown" =
78
+ mode === "countdown"
79
+ ? "countdown"
80
+ : mode === "time" || (mode === "datetime" && showTimePicker)
81
+ ? "time"
82
+ : "date";
79
83
  const iosDisplay = osDateTimeIosDisplay(pickerMode);
80
84
 
81
85
  const reset = useCallback(() => {
@@ -182,11 +186,12 @@ export function OsDateTimeSheet({
182
186
  return (
183
187
  <DateTimePicker
184
188
  value={temp}
185
- mode={pickerMode}
186
- display="default"
189
+ mode={pickerMode === "countdown" ? "time" : pickerMode}
190
+ display={pickerMode === "countdown" ? "spinner" : "default"}
191
+ is24Hour={pickerMode === "countdown" ? true : undefined}
187
192
  onChange={handleAndroidChange}
188
- minimumDate={mode !== "time" ? minDate : undefined}
189
- maximumDate={mode !== "time" ? maxDate : undefined}
193
+ minimumDate={mode === "date" || mode === "datetime" ? minDate : undefined}
194
+ maximumDate={mode === "date" || mode === "datetime" ? maxDate : undefined}
190
195
  />
191
196
  );
192
197
  }
@@ -242,8 +247,8 @@ export function OsDateTimeSheet({
242
247
  themeVariant={themeVariant}
243
248
  accentColor={accentColor}
244
249
  onChange={handleIOSChange}
245
- minimumDate={mode !== "time" ? minDate : undefined}
246
- maximumDate={mode !== "time" ? maxDate : undefined}
250
+ minimumDate={mode === "date" || mode === "datetime" ? minDate : undefined}
251
+ maximumDate={mode === "date" || mode === "datetime" ? maxDate : undefined}
247
252
  style={{ width: "100%", height: datePickerHeight(pickerMode, maxHeight) }}
248
253
  />
249
254
  </YStack>
@@ -1,4 +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";
1
+ /** iOS date is the inline calendar; time and countdown keep the wheel users expect. */
2
+ export function osDateTimeIosDisplay(mode: "date" | "time" | "countdown"): "inline" | "spinner" {
3
+ return mode === "date" ? "inline" : "spinner";
4
4
  }
@@ -10,6 +10,7 @@ import {
10
10
  dateSheetBottomPadding,
11
11
  dateSheetMaxHeight,
12
12
  DATE_PICKER_CALENDAR_HEIGHT,
13
+ DATE_PICKER_WHEEL_HEIGHT,
13
14
  DATE_SHEET_BACKDROP_STRIP,
14
15
  DATE_SHEET_MIN_HEIGHT,
15
16
  } from "./sheetKeyboard";
@@ -49,6 +50,11 @@ describe("datePickerHeight", () => {
49
50
  expect(height).toBeLessThan(DATE_PICKER_CALENDAR_HEIGHT);
50
51
  expect(height).toBeLessThanOrEqual(maxHeight);
51
52
  });
53
+
54
+ it("gives the countdown the wheel height, not the calendar's (FZ-8111)", () => {
55
+ expect(datePickerHeight("countdown", 700)).toBe(DATE_PICKER_WHEEL_HEIGHT);
56
+ expect(datePickerHeight("countdown", 700)).toBe(datePickerHeight("time", 700));
57
+ });
52
58
  });
53
59
 
54
60
  describe("dateSheetBottomPadding", () => {
@@ -39,7 +39,7 @@ export function dateSheetBottomPadding(keyboardInset: number, isIos: boolean): n
39
39
  return isIos ? 24 : 16;
40
40
  }
41
41
 
42
- export function datePickerHeight(mode: "date" | "time", maxHeight: number): number {
43
- const preferred = mode === "time" ? DATE_PICKER_WHEEL_HEIGHT : DATE_PICKER_CALENDAR_HEIGHT;
42
+ export function datePickerHeight(mode: "date" | "time" | "countdown", maxHeight: number): number {
43
+ const preferred = mode === "date" ? DATE_PICKER_CALENDAR_HEIGHT : DATE_PICKER_WHEEL_HEIGHT;
44
44
  return Math.min(preferred, Math.max(minPickerHeight, maxHeight - DATE_SHEET_CHROME));
45
45
  }
package/src/index.ts CHANGED
@@ -117,7 +117,6 @@ export {
117
117
  IconButton,
118
118
  IconButtonGroup,
119
119
  iconButtonGlyphPx,
120
- ICON_BUTTON_GLASS_MERGE_PX,
121
120
  ICON_BUTTON_GLYPH_PX,
122
121
  ICON_BUTTON_NEUTRAL_GLYPH_PX,
123
122
  ICON_BUTTON_PRESS_OPACITY,
@@ -1,23 +1,18 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { type XStackProps } from "tamagui";
3
- /**
4
- * LC-176: how far apart two Liquid Glass circles may sit and still merge. Half
5
- * the 44pt target, so neighbours on the bar's gap always join.
6
- */
7
- export declare const ICON_BUTTON_GLASS_MERGE_PX = 22;
8
3
  export interface IconButtonGroupProps extends XStackProps {
9
- /** Over imagery or video: the group shares one material capsule. */
4
+ /** Over imagery or video: the group shares one dark capsule. */
10
5
  plate?: "media";
11
6
  children?: ReactNode;
12
7
  }
13
8
  /**
14
9
  * LC-176: icon buttons that sit side by side. Grounds merge, they never stand
15
- * as a row of separate circles: on iOS 26 the bar's group is one Liquid Glass
16
- * capsule, as the system draws a bar's item group (merging each circle through
17
- * the glass container left a waist between them, FZ-8158); over media the
18
- * group shares one material capsule; a bare row stays bare. A lone button
19
- * keeps its own circle, and a bar group with no icon button in it paints no
20
- * capsule (FZ-8494).
10
+ * as a row of separate circles: on iOS 26 the bar's group sits in one system
11
+ * Liquid Glass capsule, as the system draws a bar's item group (merging each
12
+ * circle through the glass container left a waist between them, FZ-8158), so
13
+ * press and merge are the system's; over media the group shares one dark
14
+ * capsule; a bare row stays bare. A lone button keeps its own circle, and a
15
+ * bar group with no icon button in it paints no capsule (FZ-8494).
21
16
  */
22
17
  export declare function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupProps): import("react/jsx-runtime").JSX.Element;
23
18
  //# sourceMappingURL=IconButtonGroup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IconButtonGroup.d.ts","sourceRoot":"","sources":["../../src/Button/IconButtonGroup.tsx"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAMvE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,oBAAoB,2CA0GjF"}
1
+ {"version":3,"file":"IconButtonGroup.d.ts","sourceRoot":"","sources":["../../src/Button/IconButtonGroup.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAA4C,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAmBrF,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,oBAAoB,2CAyIjF"}
@@ -4,4 +4,16 @@
4
4
  * ratio, never a small glyph lost in a big plate.
5
5
  */
6
6
  export declare const ICON_BUTTON_CIRCLE_GLYPH_RATIO = 0.58;
7
+ /**
8
+ * LC-176: Phosphor bold's stem is 0.12 of its ink, an SF Symbols bold plus's
9
+ * 0.18 (measured at 17 and 24pt). An outline stroke this wide, in the 256-unit
10
+ * grid, brings a bold glyph to SF semibold-bold (0.154); a wider one closes
11
+ * Phosphor bold's 16-unit counters (the trash lid, copy's overlap).
12
+ */
13
+ export declare const GLYPH_BOLD_STROKE_UNITS = 8;
14
+ export declare function boldStroke(weight: "regular" | "bold", color?: string): {
15
+ stroke: string;
16
+ strokeWidth: number;
17
+ strokeLinejoin: "round";
18
+ } | undefined;
7
19
  //# sourceMappingURL=circleGlyph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"circleGlyph.d.ts","sourceRoot":"","sources":["../../src/Button/circleGlyph.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC"}
1
+ {"version":3,"file":"circleGlyph.d.ts","sourceRoot":"","sources":["../../src/Button/circleGlyph.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,KAAK,SAAiB;;;;cAQ5E"}
@@ -2,9 +2,10 @@ import type { ComponentType, ReactNode } from "react";
2
2
  /** The slice of expo-glass-effect's `GlassView` an icon button draws. */
3
3
  export type GlassViewComponent = ComponentType<{
4
4
  style?: unknown;
5
- glassEffectStyle?: "regular" | "clear";
5
+ glassEffectStyle?: "regular" | "clear" | "none";
6
6
  isInteractive?: boolean;
7
- pointerEvents?: "none" | "auto" | "box-none" | "box-only";
7
+ colorScheme?: "auto" | "light" | "dark";
8
+ children?: ReactNode;
8
9
  }>;
9
10
  /** The slice of expo-glass-effect's `GlassContainer` an icon button group draws. */
10
11
  export type GlassContainerComponent = ComponentType<{
@@ -14,16 +15,11 @@ export type GlassContainerComponent = ComponentType<{
14
15
  }>;
15
16
  export { isAppleTouchWeb } from "./webGlass";
16
17
  /**
17
- * LC-176: the iOS 26 Liquid Glass material an icon button floats on in a bar.
18
- * Native iOS draws it from expo-glass-effect (`glass.native.ts`; Android
19
- * probes null). On the web only iOS Safari takes the glass face (Clay on
20
- * rv_3bb3f1f65d6b981f: "Is there a way to have the buttons at the bottom be
21
- * liquid glass if it's an iPhone"); every other browser draws the bare glyph.
18
+ * LC-176: the system Liquid Glass an icon button sits in on a bar. Only native
19
+ * iOS 26 has it, from expo-glass-effect (`glass.native.ts`; Android probes
20
+ * null), so the web draws the plain glyph.
22
21
  */
23
22
  export declare function loadGlassView(): GlassViewComponent | null;
24
- /**
25
- * LC-176: the container that merges neighbouring Liquid Glass circles into one
26
- * shape, as iOS 26 groups bar buttons. Only iOS has it.
27
- */
23
+ /** LC-176: the system's Liquid Glass container. Only iOS has it. */
28
24
  export declare function loadGlassContainer(): GlassContainerComponent | null;
29
25
  //# sourceMappingURL=glass.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../../src/Button/glass.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;CAC3D,CAAC,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,uBAAuB,GAAG,aAAa,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC,CAAC;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CAEzD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAEnE"}
1
+ {"version":3,"file":"glass.d.ts","sourceRoot":"","sources":["../../src/Button/glass.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IAChD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC,CAAC;AAEH,oFAAoF;AACpF,MAAM,MAAM,uBAAuB,GAAG,aAAa,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC,CAAC;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAU7C;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CAEzD;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAEnE"}
@@ -1,17 +1,29 @@
1
1
  import type { GlassContainerComponent, GlassViewComponent } from "./glass";
2
2
  export type { GlassContainerComponent, GlassViewComponent } from "./glass";
3
+ export interface GlassSources {
4
+ core: () => {
5
+ requireOptionalNativeModule?: (moduleName: string) => unknown;
6
+ };
7
+ glass: () => {
8
+ GlassView?: GlassViewComponent;
9
+ GlassContainer?: GlassContainerComponent;
10
+ isLiquidGlassAvailable?: () => boolean;
11
+ isGlassEffectAPIAvailable?: () => boolean;
12
+ };
13
+ }
14
+ export interface SystemGlass {
15
+ view: GlassViewComponent;
16
+ container: GlassContainerComponent | null;
17
+ }
3
18
  /**
4
- * LC-176: expo-glass-effect's `GlassView`, when this binary links its native
5
- * module and the OS draws Liquid Glass (iOS 26+); Android never links it. The module is probed through
6
- * `requireOptionalNativeModule` BEFORE the guarded `require`, so an OTA on a
7
- * binary built without it, or an older iOS, falls back to the bare tinted
8
- * glyph instead of throwing mid-render (the `nativePickerLoader.ts` contract).
19
+ * LC-176: expo-glass-effect's views, when this binary links the native module
20
+ * and the OS draws Liquid Glass (iOS 26+); Android never links it. The module
21
+ * is probed through `requireOptionalNativeModule` BEFORE the guarded `require`,
22
+ * so an OTA on a binary built without it, or an older iOS, falls back to the
23
+ * bare tinted glyph instead of throwing mid-render (the `nativePickerLoader.ts`
24
+ * contract). A binary whose module predates the container keeps its capsule.
9
25
  */
26
+ export declare function resolveSystemGlass(sources?: GlassSources): SystemGlass | null;
10
27
  export declare function loadGlassView(): GlassViewComponent | null;
11
- /**
12
- * LC-176: expo-glass-effect's `GlassContainer`, present exactly when
13
- * `loadGlassView` found Liquid Glass. A binary whose module predates the
14
- * container keeps separate circles rather than throwing.
15
- */
16
28
  export declare function loadGlassContainer(): GlassContainerComponent | null;
17
29
  //# sourceMappingURL=glass.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"glass.native.d.ts","sourceRoot":"","sources":["../../src/Button/glass.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAK3E;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CA0BzD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAGnE"}
1
+ {"version":3,"file":"glass.native.d.ts","sourceRoot":"","sources":["../../src/Button/glass.native.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE3E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM;QAAE,2BAA2B,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAA;KAAE,CAAC;IAC9E,KAAK,EAAE,MAAM;QACX,SAAS,CAAC,EAAE,kBAAkB,CAAC;QAC/B,cAAc,CAAC,EAAE,uBAAuB,CAAC;QACzC,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC;QACvC,yBAAyB,CAAC,EAAE,MAAM,OAAO,CAAC;KAC3C,CAAC;CACH;AAqBD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,YAA0B,GAAG,WAAW,GAAG,IAAI,CAU1F;AASD,wBAAgB,aAAa,IAAI,kBAAkB,GAAG,IAAI,CAEzD;AAED,wBAAgB,kBAAkB,IAAI,uBAAuB,GAAG,IAAI,CAEnE"}