panelui-native 0.71.0 → 0.75.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 (306) hide show
  1. package/README.md +5 -2
  2. package/lib/module/components/accordion/index.js +10 -4
  3. package/lib/module/components/accordion/index.js.map +1 -1
  4. package/lib/module/components/area-chart/index.js +1 -0
  5. package/lib/module/components/area-chart/index.js.map +1 -1
  6. package/lib/module/components/avatar/avatar-group.js +64 -0
  7. package/lib/module/components/avatar/avatar-group.js.map +1 -0
  8. package/lib/module/components/avatar/index.js +84 -2
  9. package/lib/module/components/avatar/index.js.map +1 -1
  10. package/lib/module/components/breadcrumb/index.js +2 -1
  11. package/lib/module/components/breadcrumb/index.js.map +1 -1
  12. package/lib/module/components/button/index.js +7 -5
  13. package/lib/module/components/button/index.js.map +1 -1
  14. package/lib/module/components/carousel/index.js +1 -1
  15. package/lib/module/components/carousel/index.js.map +1 -1
  16. package/lib/module/components/chip/index.js +2 -2
  17. package/lib/module/components/chip/index.js.map +1 -1
  18. package/lib/module/components/code-block/index.js.map +1 -1
  19. package/lib/module/components/collapsible/index.js +5 -1
  20. package/lib/module/components/collapsible/index.js.map +1 -1
  21. package/lib/module/components/color-picker/color-picker-accessibility.js +12 -0
  22. package/lib/module/components/color-picker/color-picker-accessibility.js.map +1 -0
  23. package/lib/module/components/color-picker/index.js +9 -5
  24. package/lib/module/components/color-picker/index.js.map +1 -1
  25. package/lib/module/components/date-time-picker/index.js +11 -3
  26. package/lib/module/components/date-time-picker/index.js.map +1 -1
  27. package/lib/module/components/drawer/index.js +58 -13
  28. package/lib/module/components/drawer/index.js.map +1 -1
  29. package/lib/module/components/fab/index.js +7 -6
  30. package/lib/module/components/fab/index.js.map +1 -1
  31. package/lib/module/components/flow/index.js +1 -1
  32. package/lib/module/components/flow/index.js.map +1 -1
  33. package/lib/module/components/frame/index.js +2 -2
  34. package/lib/module/components/frame/index.js.map +1 -1
  35. package/lib/module/components/grid-item/index.js +2 -2
  36. package/lib/module/components/grid-item/index.js.map +1 -1
  37. package/lib/module/components/heatmap-chart/index.js +1 -0
  38. package/lib/module/components/heatmap-chart/index.js.map +1 -1
  39. package/lib/module/components/hex-chart/index.js +1 -0
  40. package/lib/module/components/hex-chart/index.js.map +1 -1
  41. package/lib/module/components/input/index.js +7 -4
  42. package/lib/module/components/input/index.js.map +1 -1
  43. package/lib/module/components/input/input-content-padding.js +6 -0
  44. package/lib/module/components/input/input-content-padding.js.map +1 -0
  45. package/lib/module/components/input-group/index.js +21 -11
  46. package/lib/module/components/input-group/index.js.map +1 -1
  47. package/lib/module/components/input-group/input-group-measurements.js +60 -0
  48. package/lib/module/components/input-group/input-group-measurements.js.map +1 -0
  49. package/lib/module/components/item/index.js +2 -2
  50. package/lib/module/components/item/index.js.map +1 -1
  51. package/lib/module/components/line-chart/index.js +1 -0
  52. package/lib/module/components/line-chart/index.js.map +1 -1
  53. package/lib/module/components/marker/index.js +2 -2
  54. package/lib/module/components/marker/index.js.map +1 -1
  55. package/lib/module/components/marquee/index.js +9 -12
  56. package/lib/module/components/marquee/index.js.map +1 -1
  57. package/lib/module/components/marquee/marquee-math.js +39 -0
  58. package/lib/module/components/marquee/marquee-math.js.map +1 -0
  59. package/lib/module/components/menu/index.js +11 -3
  60. package/lib/module/components/menu/index.js.map +1 -1
  61. package/lib/module/components/otp-input/index.js +3 -1
  62. package/lib/module/components/otp-input/index.js.map +1 -1
  63. package/lib/module/components/otp-input/otp-input-state.js +7 -0
  64. package/lib/module/components/otp-input/otp-input-state.js.map +1 -0
  65. package/lib/module/components/pagination/index.js +27 -11
  66. package/lib/module/components/pagination/index.js.map +1 -1
  67. package/lib/module/components/panelside/index.js +7 -2
  68. package/lib/module/components/panelside/index.js.map +1 -1
  69. package/lib/module/components/planner/index.js +46 -11
  70. package/lib/module/components/planner/index.js.map +1 -1
  71. package/lib/module/components/planner/planner-today.js +8 -0
  72. package/lib/module/components/planner/planner-today.js.map +1 -0
  73. package/lib/module/components/plot/index.js +113 -53
  74. package/lib/module/components/plot/index.js.map +1 -1
  75. package/lib/module/components/plot/plot-series-registry.js +32 -0
  76. package/lib/module/components/plot/plot-series-registry.js.map +1 -0
  77. package/lib/module/components/post/index.js +4 -4
  78. package/lib/module/components/post/index.js.map +1 -1
  79. package/lib/module/components/radio-group/index.js +2 -1
  80. package/lib/module/components/radio-group/index.js.map +1 -1
  81. package/lib/module/components/radio-group/radio-group-value.js +8 -0
  82. package/lib/module/components/radio-group/radio-group-value.js.map +1 -0
  83. package/lib/module/components/rating/index.js +7 -4
  84. package/lib/module/components/rating/index.js.map +1 -1
  85. package/lib/module/components/rating/rating-inputs.js +15 -0
  86. package/lib/module/components/rating/rating-inputs.js.map +1 -0
  87. package/lib/module/components/search-bar/index.js +331 -0
  88. package/lib/module/components/search-bar/index.js.map +1 -0
  89. package/lib/module/components/search-bar/search-bar-debounce.js +23 -0
  90. package/lib/module/components/search-bar/search-bar-debounce.js.map +1 -0
  91. package/lib/module/components/select/index.js +9 -1
  92. package/lib/module/components/select/index.js.map +1 -1
  93. package/lib/module/components/select/native-select-contract.js +11 -0
  94. package/lib/module/components/select/native-select-contract.js.map +1 -0
  95. package/lib/module/components/split-view/index.js +439 -0
  96. package/lib/module/components/split-view/index.js.map +1 -0
  97. package/lib/module/components/split-view/split-view-math.js +132 -0
  98. package/lib/module/components/split-view/split-view-math.js.map +1 -0
  99. package/lib/module/components/splitter/index.js +433 -0
  100. package/lib/module/components/splitter/index.js.map +1 -0
  101. package/lib/module/components/splitter/splitter-math.js +206 -0
  102. package/lib/module/components/splitter/splitter-math.js.map +1 -0
  103. package/lib/module/components/steps/index.js +16 -5
  104. package/lib/module/components/steps/index.js.map +1 -1
  105. package/lib/module/components/steps/steps-trigger.js +6 -0
  106. package/lib/module/components/steps/steps-trigger.js.map +1 -0
  107. package/lib/module/components/table/index.js +3 -4
  108. package/lib/module/components/table/index.js.map +1 -1
  109. package/lib/module/components/tag-input/index.js +15 -10
  110. package/lib/module/components/tag-input/index.js.map +1 -1
  111. package/lib/module/components/tag-input/tag-input-state.js +14 -0
  112. package/lib/module/components/tag-input/tag-input-state.js.map +1 -0
  113. package/lib/module/components/textarea/index.js +9 -4
  114. package/lib/module/components/textarea/index.js.map +1 -1
  115. package/lib/module/components/textarea/textarea-disabled.js +6 -0
  116. package/lib/module/components/textarea/textarea-disabled.js.map +1 -0
  117. package/lib/module/components/timeline/index.js +352 -47
  118. package/lib/module/components/timeline/index.js.map +1 -1
  119. package/lib/module/components/timeline/timeline-geometry.js +11 -0
  120. package/lib/module/components/timeline/timeline-geometry.js.map +1 -0
  121. package/lib/module/components/toast/index.js +18 -1
  122. package/lib/module/components/toast/index.js.map +1 -1
  123. package/lib/module/components/toast/toast-store.js +36 -4
  124. package/lib/module/components/toast/toast-store.js.map +1 -1
  125. package/lib/module/components/toggle-button/index.js +1 -1
  126. package/lib/module/components/toggle-button/index.js.map +1 -1
  127. package/lib/module/components/tour/index.js +4 -2
  128. package/lib/module/components/tour/index.js.map +1 -1
  129. package/lib/module/components/tour/tour-card-measurement.js +24 -0
  130. package/lib/module/components/tour/tour-card-measurement.js.map +1 -0
  131. package/lib/module/components/tree/index.js +13 -8
  132. package/lib/module/components/tree/index.js.map +1 -1
  133. package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
  134. package/lib/module/index.js +3 -0
  135. package/lib/module/index.js.map +1 -1
  136. package/lib/module/utils/chart.js +50 -7
  137. package/lib/module/utils/chart.js.map +1 -1
  138. package/lib/typescript/src/components/accordion/index.d.ts +2 -2
  139. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  140. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/avatar/avatar-group.d.ts +46 -0
  142. package/lib/typescript/src/components/avatar/avatar-group.d.ts.map +1 -0
  143. package/lib/typescript/src/components/avatar/index.d.ts +23 -1
  144. package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
  146. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  147. package/lib/typescript/src/components/code-block/index.d.ts +3 -3
  148. package/lib/typescript/src/components/code-block/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/collapsible/index.d.ts +2 -2
  150. package/lib/typescript/src/components/collapsible/index.d.ts.map +1 -1
  151. package/lib/typescript/src/components/color-picker/color-picker-accessibility.d.ts +8 -0
  152. package/lib/typescript/src/components/color-picker/color-picker-accessibility.d.ts.map +1 -0
  153. package/lib/typescript/src/components/color-picker/index.d.ts.map +1 -1
  154. package/lib/typescript/src/components/date-time-picker/index.d.ts.map +1 -1
  155. package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
  156. package/lib/typescript/src/components/fab/index.d.ts +4 -2
  157. package/lib/typescript/src/components/fab/index.d.ts.map +1 -1
  158. package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
  159. package/lib/typescript/src/components/hex-chart/index.d.ts.map +1 -1
  160. package/lib/typescript/src/components/input/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/input/input-content-padding.d.ts +2 -0
  162. package/lib/typescript/src/components/input/input-content-padding.d.ts.map +1 -0
  163. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  164. package/lib/typescript/src/components/input-group/input-group-measurements.d.ts +16 -0
  165. package/lib/typescript/src/components/input-group/input-group-measurements.d.ts.map +1 -0
  166. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/marquee/index.d.ts +1 -1
  168. package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
  169. package/lib/typescript/src/components/marquee/marquee-math.d.ts +21 -0
  170. package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -0
  171. package/lib/typescript/src/components/menu/index.d.ts +1 -1
  172. package/lib/typescript/src/components/menu/index.d.ts.map +1 -1
  173. package/lib/typescript/src/components/otp-input/index.d.ts.map +1 -1
  174. package/lib/typescript/src/components/otp-input/otp-input-state.d.ts +3 -0
  175. package/lib/typescript/src/components/otp-input/otp-input-state.d.ts.map +1 -0
  176. package/lib/typescript/src/components/pagination/index.d.ts.map +1 -1
  177. package/lib/typescript/src/components/panelside/index.d.ts +1 -1
  178. package/lib/typescript/src/components/panelside/index.d.ts.map +1 -1
  179. package/lib/typescript/src/components/plan/index.d.ts +2 -2
  180. package/lib/typescript/src/components/planner/index.d.ts.map +1 -1
  181. package/lib/typescript/src/components/planner/planner-today.d.ts +2 -0
  182. package/lib/typescript/src/components/planner/planner-today.d.ts.map +1 -0
  183. package/lib/typescript/src/components/plot/index.d.ts +60 -6
  184. package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
  185. package/lib/typescript/src/components/plot/plot-series-registry.d.ts +8 -0
  186. package/lib/typescript/src/components/plot/plot-series-registry.d.ts.map +1 -0
  187. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  188. package/lib/typescript/src/components/radio-group/radio-group-value.d.ts +2 -0
  189. package/lib/typescript/src/components/radio-group/radio-group-value.d.ts.map +1 -0
  190. package/lib/typescript/src/components/rating/index.d.ts.map +1 -1
  191. package/lib/typescript/src/components/rating/rating-inputs.d.ts +4 -0
  192. package/lib/typescript/src/components/rating/rating-inputs.d.ts.map +1 -0
  193. package/lib/typescript/src/components/search-bar/index.d.ts +216 -0
  194. package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -0
  195. package/lib/typescript/src/components/search-bar/search-bar-debounce.d.ts +7 -0
  196. package/lib/typescript/src/components/search-bar/search-bar-debounce.d.ts.map +1 -0
  197. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  198. package/lib/typescript/src/components/select/native-select-contract.d.ts +11 -0
  199. package/lib/typescript/src/components/select/native-select-contract.d.ts.map +1 -0
  200. package/lib/typescript/src/components/split-view/index.d.ts +159 -0
  201. package/lib/typescript/src/components/split-view/index.d.ts.map +1 -0
  202. package/lib/typescript/src/components/split-view/split-view-math.d.ts +54 -0
  203. package/lib/typescript/src/components/split-view/split-view-math.d.ts.map +1 -0
  204. package/lib/typescript/src/components/splitter/index.d.ts +78 -0
  205. package/lib/typescript/src/components/splitter/index.d.ts.map +1 -0
  206. package/lib/typescript/src/components/splitter/splitter-math.d.ts +67 -0
  207. package/lib/typescript/src/components/splitter/splitter-math.d.ts.map +1 -0
  208. package/lib/typescript/src/components/steps/index.d.ts +2 -2
  209. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  210. package/lib/typescript/src/components/steps/steps-trigger.d.ts +2 -0
  211. package/lib/typescript/src/components/steps/steps-trigger.d.ts.map +1 -0
  212. package/lib/typescript/src/components/tag-input/index.d.ts.map +1 -1
  213. package/lib/typescript/src/components/tag-input/tag-input-state.d.ts +12 -0
  214. package/lib/typescript/src/components/tag-input/tag-input-state.d.ts.map +1 -0
  215. package/lib/typescript/src/components/textarea/index.d.ts.map +1 -1
  216. package/lib/typescript/src/components/textarea/textarea-disabled.d.ts +2 -0
  217. package/lib/typescript/src/components/textarea/textarea-disabled.d.ts.map +1 -0
  218. package/lib/typescript/src/components/timeline/index.d.ts +36 -2
  219. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  220. package/lib/typescript/src/components/timeline/timeline-geometry.d.ts +4 -0
  221. package/lib/typescript/src/components/timeline/timeline-geometry.d.ts.map +1 -0
  222. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  223. package/lib/typescript/src/components/toast/toast-store.d.ts +7 -2
  224. package/lib/typescript/src/components/toast/toast-store.d.ts.map +1 -1
  225. package/lib/typescript/src/components/tour/index.d.ts.map +1 -1
  226. package/lib/typescript/src/components/tour/tour-card-measurement.d.ts +14 -0
  227. package/lib/typescript/src/components/tour/tour-card-measurement.d.ts.map +1 -0
  228. package/lib/typescript/src/components/tree/index.d.ts +3 -4
  229. package/lib/typescript/src/components/tree/index.d.ts.map +1 -1
  230. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +2 -2
  231. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
  232. package/lib/typescript/src/index.d.ts +5 -2
  233. package/lib/typescript/src/index.d.ts.map +1 -1
  234. package/lib/typescript/src/utils/chart.d.ts +28 -3
  235. package/lib/typescript/src/utils/chart.d.ts.map +1 -1
  236. package/package.json +1 -1
  237. package/src/components/accordion/index.tsx +17 -7
  238. package/src/components/area-chart/index.tsx +8 -0
  239. package/src/components/avatar/avatar-group.ts +78 -0
  240. package/src/components/avatar/index.tsx +121 -1
  241. package/src/components/breadcrumb/index.tsx +2 -1
  242. package/src/components/button/index.tsx +3 -2
  243. package/src/components/carousel/index.tsx +1 -1
  244. package/src/components/chip/index.tsx +2 -2
  245. package/src/components/code-block/index.tsx +3 -3
  246. package/src/components/collapsible/index.tsx +13 -4
  247. package/src/components/color-picker/color-picker-accessibility.ts +12 -0
  248. package/src/components/color-picker/index.tsx +30 -10
  249. package/src/components/date-time-picker/index.tsx +11 -3
  250. package/src/components/drawer/index.tsx +42 -14
  251. package/src/components/fab/index.tsx +21 -11
  252. package/src/components/flow/index.tsx +1 -1
  253. package/src/components/frame/index.tsx +2 -2
  254. package/src/components/grid-item/index.tsx +2 -2
  255. package/src/components/heatmap-chart/index.tsx +7 -1
  256. package/src/components/hex-chart/index.tsx +7 -1
  257. package/src/components/input/index.tsx +6 -2
  258. package/src/components/input/input-content-padding.ts +3 -0
  259. package/src/components/input-group/index.tsx +45 -12
  260. package/src/components/input-group/input-group-measurements.ts +98 -0
  261. package/src/components/item/index.tsx +2 -2
  262. package/src/components/line-chart/index.tsx +9 -0
  263. package/src/components/marker/index.tsx +2 -2
  264. package/src/components/marquee/index.tsx +14 -12
  265. package/src/components/marquee/marquee-math.ts +37 -0
  266. package/src/components/menu/index.tsx +11 -3
  267. package/src/components/otp-input/index.tsx +3 -1
  268. package/src/components/otp-input/otp-input-state.ts +7 -0
  269. package/src/components/pagination/index.tsx +26 -13
  270. package/src/components/panelside/index.tsx +7 -2
  271. package/src/components/plan/index.tsx +2 -2
  272. package/src/components/planner/index.tsx +44 -11
  273. package/src/components/planner/planner-today.ts +5 -0
  274. package/src/components/plot/index.tsx +199 -51
  275. package/src/components/plot/plot-series-registry.ts +41 -0
  276. package/src/components/post/index.tsx +4 -4
  277. package/src/components/radio-group/index.tsx +4 -1
  278. package/src/components/radio-group/radio-group-value.ts +9 -0
  279. package/src/components/rating/index.tsx +11 -4
  280. package/src/components/rating/rating-inputs.ts +19 -0
  281. package/src/components/search-bar/index.tsx +441 -0
  282. package/src/components/search-bar/search-bar-debounce.ts +35 -0
  283. package/src/components/select/index.tsx +10 -1
  284. package/src/components/select/native-select-contract.ts +12 -0
  285. package/src/components/split-view/index.tsx +565 -0
  286. package/src/components/split-view/split-view-math.ts +146 -0
  287. package/src/components/splitter/index.tsx +589 -0
  288. package/src/components/splitter/splitter-math.ts +244 -0
  289. package/src/components/steps/index.tsx +35 -9
  290. package/src/components/steps/steps-trigger.ts +6 -0
  291. package/src/components/table/index.tsx +2 -2
  292. package/src/components/tag-input/index.tsx +13 -11
  293. package/src/components/tag-input/tag-input-state.ts +18 -0
  294. package/src/components/textarea/index.tsx +8 -4
  295. package/src/components/textarea/textarea-disabled.ts +6 -0
  296. package/src/components/timeline/index.tsx +405 -46
  297. package/src/components/timeline/timeline-geometry.ts +9 -0
  298. package/src/components/toast/index.tsx +26 -1
  299. package/src/components/toast/toast-store.ts +43 -8
  300. package/src/components/toggle-button/index.tsx +1 -1
  301. package/src/components/tour/index.tsx +11 -3
  302. package/src/components/tour/tour-card-measurement.ts +29 -0
  303. package/src/components/tree/index.tsx +17 -12
  304. package/src/hooks/use-keyboard-avoidance.ts +7 -2
  305. package/src/index.ts +19 -0
  306. package/src/utils/chart.ts +66 -7
@@ -0,0 +1,244 @@
1
+ /**
2
+ * The arithmetic a splitter runs while a seam is under a finger.
3
+ *
4
+ * Sizes are percentages of the whole splitter, never points, which is what
5
+ * makes a dragged layout survive a rotation or a window resize: the numbers
6
+ * mean the same thing at any container size, so nothing has to be re-measured
7
+ * and re-derived when one changes.
8
+ *
9
+ * It lives apart from the component for two reasons. It is the part with the
10
+ * edge cases — a pair of panels that cannot both honour their minimums, a
11
+ * collapse that has to decide between shut and its floor, a caller's sizes that
12
+ * do not add up — and it is the part that runs on the UI thread, where a wrong
13
+ * answer is a layout that sticks rather than an exception anybody sees.
14
+ */
15
+
16
+ /** How small a panel may get before its neighbour stops taking room, in percent. */
17
+ export const DEFAULT_MIN_SIZE = 10;
18
+ /** How large a panel may get, in percent. */
19
+ export const DEFAULT_MAX_SIZE = 100;
20
+
21
+ export interface SplitterConstraint {
22
+ /** Smallest share the panel may hold while open, in percent. */
23
+ minSize: number;
24
+ /** Largest share the panel may hold, in percent. */
25
+ maxSize: number;
26
+ /** Whether dragging past the minimum shuts the panel instead of stopping. */
27
+ collapsible: boolean;
28
+ /** Share the panel holds while shut, in percent. */
29
+ collapsedSize: number;
30
+ }
31
+
32
+ export interface SplitterConstraintInput {
33
+ minSize?: number;
34
+ maxSize?: number;
35
+ collapsible?: boolean;
36
+ collapsedSize?: number;
37
+ }
38
+
39
+ function clamp(value: number, low: number, high: number): number {
40
+ 'worklet';
41
+ return Math.min(Math.max(value, low), high);
42
+ }
43
+
44
+ function finite(value: number | undefined, fallback: number): number {
45
+ 'worklet';
46
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
47
+ }
48
+
49
+ /** The smallest share a panel can hold at all — its floor once shut, or its minimum. */
50
+ export function panelFloor(constraint: SplitterConstraint): number {
51
+ 'worklet';
52
+ return constraint.collapsible ? constraint.collapsedSize : constraint.minSize;
53
+ }
54
+
55
+ /**
56
+ * Fills a panel's declared limits in, in an order that cannot contradict
57
+ * itself: the maximum is never below the minimum, and a collapsed size is never
58
+ * above the minimum it exists to sit below.
59
+ */
60
+ export function normalizeConstraint(
61
+ input: SplitterConstraintInput | undefined
62
+ ): SplitterConstraint {
63
+ const minSize = clamp(finite(input?.minSize, DEFAULT_MIN_SIZE), 0, 100);
64
+ const maxSize = clamp(finite(input?.maxSize, DEFAULT_MAX_SIZE), minSize, 100);
65
+ const collapsible = input?.collapsible === true;
66
+ const collapsedSize = clamp(finite(input?.collapsedSize, 0), 0, minSize);
67
+ return { minSize, maxSize, collapsible, collapsedSize };
68
+ }
69
+
70
+ function total(layout: number[]): number {
71
+ 'worklet';
72
+ let sum = 0;
73
+ for (let index = 0; index < layout.length; index += 1) sum += layout[index]!;
74
+ return sum;
75
+ }
76
+
77
+ /**
78
+ * Pushes a layout onto exactly 100, moving each panel by its share of the room
79
+ * it has left in the direction the correction is going. Panels already against
80
+ * a limit therefore absorb none of it, which is what keeps a correction from
81
+ * quietly undoing the limit it just honoured.
82
+ *
83
+ * Four passes, because clamping after each one can hand the next a smaller
84
+ * remainder rather than none; it converges long before that in practice, and
85
+ * the bound is what stops a contradictory set of limits from spinning.
86
+ */
87
+ function settle(layout: number[], constraints: SplitterConstraint[]): number[] {
88
+ 'worklet';
89
+ const next = layout.slice();
90
+
91
+ for (let pass = 0; pass < 4; pass += 1) {
92
+ const remainder = 100 - total(next);
93
+ if (Math.abs(remainder) < 1e-6) break;
94
+
95
+ let room = 0;
96
+ const headroom: number[] = [];
97
+ for (let index = 0; index < next.length; index += 1) {
98
+ const constraint = constraints[index]!;
99
+ const available =
100
+ remainder > 0
101
+ ? constraint.maxSize - next[index]!
102
+ : next[index]! - panelFloor(constraint);
103
+ headroom.push(Math.max(available, 0));
104
+ room += Math.max(available, 0);
105
+ }
106
+ if (room <= 1e-6) break;
107
+
108
+ for (let index = 0; index < next.length; index += 1) {
109
+ next[index] = clamp(
110
+ next[index]! + (remainder * headroom[index]!) / room,
111
+ panelFloor(constraints[index]!),
112
+ constraints[index]!.maxSize
113
+ );
114
+ }
115
+ }
116
+
117
+ // Minimums that add up to more than the whole cannot all be honoured. Scaling
118
+ // the whole layout down is the failure that stays legible: every panel is
119
+ // smaller than it asked for, rather than the last one being pushed out of the
120
+ // container where nobody can see that it is there at all.
121
+ const sum = total(next);
122
+ if (sum > 100 + 1e-6) {
123
+ for (let index = 0; index < next.length; index += 1) {
124
+ next[index] = (next[index]! * 100) / sum;
125
+ }
126
+ }
127
+
128
+ return next;
129
+ }
130
+
131
+ /**
132
+ * Turns whatever sizes a caller gave into one layout that adds up.
133
+ *
134
+ * Panels with no size of their own split what the sized ones left over, so a
135
+ * three-pane splitter that only pins its sidebar gets the sensible reading of
136
+ * that: the sidebar keeps its number and the other two share the rest.
137
+ */
138
+ export function resolveLayout(
139
+ sizes: (number | undefined)[],
140
+ constraints: SplitterConstraint[]
141
+ ): number[] {
142
+ if (constraints.length === 0) return [];
143
+
144
+ let claimed = 0;
145
+ let unsized = 0;
146
+ for (let index = 0; index < constraints.length; index += 1) {
147
+ const size = sizes[index];
148
+ if (typeof size === 'number' && Number.isFinite(size)) {
149
+ claimed += clamp(size, 0, 100);
150
+ } else {
151
+ unsized += 1;
152
+ }
153
+ }
154
+
155
+ const share = unsized > 0 ? Math.max(100 - claimed, 0) / unsized : 0;
156
+ const layout: number[] = [];
157
+ for (let index = 0; index < constraints.length; index += 1) {
158
+ const size = sizes[index];
159
+ const raw =
160
+ typeof size === 'number' && Number.isFinite(size) ? clamp(size, 0, 100) : share;
161
+ layout.push(
162
+ clamp(raw, panelFloor(constraints[index]!), constraints[index]!.maxSize)
163
+ );
164
+ }
165
+
166
+ return settle(layout, constraints);
167
+ }
168
+
169
+ /**
170
+ * A panel's size once it is below its minimum: shut if it is collapsible and
171
+ * the drag has gone more than halfway there, and otherwise held at the minimum.
172
+ *
173
+ * The halfway point is what makes collapsing feel like a decision rather than
174
+ * an accident — a finger that grazes the minimum springs back, and one that
175
+ * keeps going shuts the panel.
176
+ */
177
+ function snapDown(size: number, constraint: SplitterConstraint): number {
178
+ 'worklet';
179
+ if (size >= constraint.minSize) return size;
180
+ if (!constraint.collapsible) return constraint.minSize;
181
+ return size < (constraint.collapsedSize + constraint.minSize) / 2
182
+ ? constraint.collapsedSize
183
+ : constraint.minSize;
184
+ }
185
+
186
+ /**
187
+ * Moves one seam, in percent, and hands back the whole layout.
188
+ *
189
+ * Only the two panels either side of the seam change: a drag borrows from its
190
+ * neighbour and nobody else, so the panels further along stay exactly where the
191
+ * reader left them.
192
+ *
193
+ * Collapsing is only ever offered to the panel being shrunk. Both sides could
194
+ * technically be past their minimum at once at the end of a long drag, and
195
+ * shutting the panel that is currently growing is never what the drag meant.
196
+ */
197
+ export function resizeLayout(
198
+ layout: number[],
199
+ boundary: number,
200
+ delta: number,
201
+ constraints: SplitterConstraint[]
202
+ ): number[] {
203
+ 'worklet';
204
+ const next = layout.slice();
205
+ if (boundary < 0 || boundary + 1 >= next.length) return next;
206
+ if (!Number.isFinite(delta)) return next;
207
+
208
+ const first = constraints[boundary];
209
+ const second = constraints[boundary + 1];
210
+ if (!first || !second) return next;
211
+
212
+ const pair = next[boundary]! + next[boundary + 1]!;
213
+ let intended = next[boundary]! + delta;
214
+
215
+ if (delta < 0) {
216
+ intended = snapDown(intended, first);
217
+ } else if (delta > 0) {
218
+ intended = pair - snapDown(pair - intended, second);
219
+ }
220
+
221
+ const low = Math.max(panelFloor(first), pair - second.maxSize);
222
+ const high = Math.min(first.maxSize, pair - panelFloor(second));
223
+ if (low > high) return next;
224
+
225
+ next[boundary] = clamp(intended, low, high);
226
+ next[boundary + 1] = pair - next[boundary]!;
227
+ return next;
228
+ }
229
+
230
+ /** Where a seam sits, in percent, measured from the start of the splitter. */
231
+ export function layoutOffset(layout: number[], boundary: number): number {
232
+ 'worklet';
233
+ let offset = 0;
234
+ for (let index = 0; index <= boundary && index < layout.length; index += 1) {
235
+ offset += layout[index]!;
236
+ }
237
+ return offset;
238
+ }
239
+
240
+ /** Whether a panel is currently shut rather than merely small. */
241
+ export function isCollapsed(size: number, constraint: SplitterConstraint): boolean {
242
+ 'worklet';
243
+ return constraint.collapsible && size <= constraint.collapsedSize + 1e-6;
244
+ }
@@ -38,12 +38,19 @@ import {
38
38
  useState,
39
39
  type ReactNode,
40
40
  } from 'react';
41
- import { Pressable, View, type Text as RNText, type ViewProps } from 'react-native';
41
+ import {
42
+ Pressable,
43
+ View,
44
+ type PressableProps,
45
+ type Text as RNText,
46
+ type ViewProps,
47
+ } from 'react-native';
42
48
  import { tv } from 'tailwind-variants';
43
49
  import { useCSSVariable } from 'uniwind';
44
50
  import { CheckIcon } from '../../icons';
45
51
  import { Text, type TextProps, textChildren } from '../../primitives/text';
46
52
  import { Spinner } from '../spinner';
53
+ import { stepsTriggerDisabled } from './steps-trigger';
47
54
 
48
55
  export type StepState = 'active' | 'completed' | 'inactive' | 'loading';
49
56
  export type StepsOrientation = 'horizontal' | 'vertical';
@@ -293,7 +300,7 @@ const StepsItem = forwardRef<View, StepsItemProps>(
293
300
  );
294
301
  StepsItem.displayName = 'Steps.Item';
295
302
 
296
- export interface StepsTriggerProps extends ViewProps {
303
+ export interface StepsTriggerProps extends PressableProps {
297
304
  className?: string;
298
305
  children?: ReactNode;
299
306
  }
@@ -308,11 +315,23 @@ const STATE_WORDS: Record<StepState, string> = {
308
315
 
309
316
  /** Makes its item selectable. Omit it for a read-only stepper. */
310
317
  const StepsTrigger = forwardRef<View, StepsTriggerProps>(
311
- ({ className, children, ...props }, ref) => {
318
+ (
319
+ {
320
+ className,
321
+ children,
322
+ disabled,
323
+ onPress,
324
+ accessibilityState,
325
+ accessibilityValue,
326
+ ...props
327
+ },
328
+ ref
329
+ ) => {
312
330
  const { setActiveStep, orientation } = useSteps('Steps.Trigger');
313
331
  const { step, state, isDisabled } = useStepItem('Steps.Trigger');
314
332
  const placement = useContext(StepPositionContext);
315
- const { trigger } = stepsVariants({ orientation, state, isDisabled });
333
+ const triggerDisabled = stepsTriggerDisabled(isDisabled, disabled);
334
+ const { trigger } = stepsVariants({ orientation, state, isDisabled: triggerDisabled });
316
335
 
317
336
  /*
318
337
  * Said as a value rather than a label, because the label is the step's own
@@ -327,13 +346,20 @@ const StepsTrigger = forwardRef<View, StepsTriggerProps>(
327
346
  return (
328
347
  <Pressable
329
348
  ref={ref}
349
+ {...props}
330
350
  accessibilityRole="button"
331
- accessibilityState={{ disabled: isDisabled, selected: state === 'active' }}
332
- accessibilityValue={{ text: position }}
333
- disabled={isDisabled}
334
- onPress={() => setActiveStep(step)}
351
+ accessibilityState={{
352
+ ...accessibilityState,
353
+ disabled: triggerDisabled,
354
+ selected: state === 'active',
355
+ }}
356
+ accessibilityValue={{ ...accessibilityValue, text: position }}
357
+ disabled={triggerDisabled}
358
+ onPress={(event) => {
359
+ onPress?.(event);
360
+ setActiveStep(step);
361
+ }}
335
362
  className={trigger({ className })}
336
- {...props}
337
363
  >
338
364
  {textChildren(children)}
339
365
  </Pressable>
@@ -0,0 +1,6 @@
1
+ export function stepsTriggerDisabled(
2
+ itemDisabled: boolean,
3
+ triggerDisabled: boolean | null | undefined
4
+ ): boolean {
5
+ return itemDisabled || !!triggerDisabled;
6
+ }
@@ -414,10 +414,10 @@ const TableRow = forwardRef<View, TableRowProps>(
414
414
  return (
415
415
  <View
416
416
  ref={ref}
417
+ {...(props as ViewProps)}
417
418
  role="row"
418
419
  accessibilityState={{ disabled: !!disabled, selected: !!selected }}
419
420
  className={classes}
420
- {...(props as ViewProps)}
421
421
  >
422
422
  {cells}
423
423
  </View>
@@ -427,6 +427,7 @@ const TableRow = forwardRef<View, TableRowProps>(
427
427
  return (
428
428
  <AnimatedPressable
429
429
  ref={ref}
430
+ {...props}
430
431
  // A row you can act on is a button first: announcing it as a row would
431
432
  // hide the fact that it does anything.
432
433
  accessibilityRole="button"
@@ -434,7 +435,6 @@ const TableRow = forwardRef<View, TableRowProps>(
434
435
  disabled={disabled}
435
436
  onPress={onPress}
436
437
  className={classes}
437
- {...props}
438
438
  >
439
439
  {cells}
440
440
  </AnimatedPressable>
@@ -64,6 +64,7 @@ import { Text } from '../../primitives/text';
64
64
  import { selectionTick } from '../../utils/haptics';
65
65
  import { Chip, type ChipSize, type ChipVariant } from '../chip';
66
66
  import { Label } from '../label';
67
+ import { reconcileMarkedTag, type MarkedTag } from './tag-input-state';
67
68
 
68
69
  /** Matches Input and Combobox, so the three read as the same control focused. */
69
70
  const FOCUS_DURATION = 150;
@@ -298,18 +299,18 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
298
299
  * itself, because with `allowDuplicates` two tags can read the same and
299
300
  * marking "the last one" has to mean the last one.
300
301
  */
301
- const [marked, setMarked] = useState<number | null>(null);
302
+ const [marked, setMarked] = useState<MarkedTag | null>(null);
302
303
 
303
304
  const overflowing = max !== undefined && tags.length > max;
304
305
  const invalid = !!errorMessage || overflowing;
305
306
  const interactive = !disabled && !readOnly;
306
307
 
307
- // The list can be emptied from outside, which would leave the mark pointing
308
- // past the end of it — and a stale mark is a tag deleted by a backspace
309
- // meant for the one that used to be there.
308
+ // A controlled list can be replaced or reordered between the two
309
+ // backspaces. Keep the mark only while the exact tag it armed still owns
310
+ // that slot, so a key press aimed at the old value cannot delete the new one.
310
311
  useEffect(() => {
311
- if (marked !== null && marked >= tags.length) setMarked(null);
312
- }, [marked, tags.length]);
312
+ setMarked((current) => reconcileMarkedTag(current, tags));
313
+ }, [tags]);
313
314
 
314
315
  const setText = useCallback(
315
316
  (next: string) => {
@@ -418,14 +419,15 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
418
419
  // what it always means, and the tags are none of its business.
419
420
  if (text.length > 0 || tags.length === 0) return;
420
421
 
421
- if (marked !== null && marked < tags.length) {
422
- removeAt(marked);
422
+ if (marked !== null && reconcileMarkedTag(marked, tags)) {
423
+ removeAt(marked.index);
423
424
  setMarked(null);
424
425
  return;
425
426
  }
426
- setMarked(tags.length - 1);
427
+ const index = tags.length - 1;
428
+ setMarked({ index, tag: tags[index]! });
427
429
  },
428
- [onKeyPress, text.length, tags.length, marked, removeAt]
430
+ [onKeyPress, text.length, tags, marked, removeAt]
429
431
  );
430
432
 
431
433
  const handleFocus = useCallback<NonNullable<TextInputProps['onFocus']>>(
@@ -541,7 +543,7 @@ export const TagInput = forwardRef<TextInput, TagInputProps>(
541
543
  // The marked tag turns destructive rather than growing an
542
544
  // outline: it is about to be deleted, and that is the one
543
545
  // colour in the theme that already means exactly that.
544
- variant={marked === index ? 'destructive' : chipVariant}
546
+ variant={marked?.index === index ? 'destructive' : chipVariant}
545
547
  onClose={interactive ? () => removeAt(index) : undefined}
546
548
  closeLabel={`Remove ${tag}`}
547
549
  haptics={haptics}
@@ -0,0 +1,18 @@
1
+ /** The exact slot a first Backspace armed for removal. */
2
+ export interface MarkedTag {
3
+ index: number;
4
+ tag: string;
5
+ }
6
+
7
+ /**
8
+ * Keeps an armed removal only while the same tag still occupies the same slot.
9
+ * Controlled fields can replace or reorder their value between key presses;
10
+ * a mark from the old value must never authorize deleting the new one.
11
+ */
12
+ export function reconcileMarkedTag(
13
+ marked: MarkedTag | null,
14
+ tags: readonly string[]
15
+ ): MarkedTag | null {
16
+ if (marked === null) return null;
17
+ return tags[marked.index] === marked.tag ? marked : null;
18
+ }
@@ -39,6 +39,7 @@ import type { KeyboardAvoidanceMode } from '../../hooks/use-keyboard-avoidance';
39
39
  import { KeyboardAvoider } from '../../primitives/keyboard-avoider';
40
40
  import { Text } from '../../primitives/text';
41
41
  import { Label } from '../label';
42
+ import { textareaIsDisabled } from './textarea-disabled';
42
43
 
43
44
  /** Long enough to read as a transition, short enough not to lag a fast tab. */
44
45
  const FOCUS_DURATION = 150;
@@ -183,6 +184,8 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
183
184
  onFocus,
184
185
  onBlur,
185
186
  onContentSizeChange,
187
+ editable,
188
+ accessibilityState,
186
189
  style,
187
190
  ...props
188
191
  },
@@ -190,6 +193,7 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
190
193
  ) => {
191
194
  const [focused, setFocused] = useState(false);
192
195
  const invalid = !!errorMessage;
196
+ const isDisabled = textareaIsDisabled(disabled, editable);
193
197
 
194
198
  const metrics = METRICS[size];
195
199
  /* Both paddings plus the lines themselves — the box a row count asks for. */
@@ -225,7 +229,7 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
225
229
  const slots = textareaVariants({
226
230
  variant,
227
231
  size,
228
- disabled: !!disabled,
232
+ disabled: isDisabled,
229
233
  over,
230
234
  });
231
235
 
@@ -320,19 +324,19 @@ export const Textarea = forwardRef<TextInput, TextareaProps>(
320
324
  const body = (
321
325
  <>
322
326
  {label ? (
323
- <Label isRequired={isRequired} isInvalid={invalid} isDisabled={!!disabled}>
327
+ <Label isRequired={isRequired} isInvalid={invalid} isDisabled={isDisabled}>
324
328
  {label}
325
329
  </Label>
326
330
  ) : null}
327
331
  <AnimatedTextInput
328
332
  ref={ref}
329
333
  multiline
330
- editable={!disabled}
334
+ editable={!isDisabled}
331
335
  onFocus={handleFocus}
332
336
  onBlur={handleBlur}
333
337
  onContentSizeChange={handleContentSizeChange}
334
338
  accessibilityLabel={label}
335
- accessibilityState={{ disabled: !!disabled }}
339
+ accessibilityState={{ ...accessibilityState, disabled: isDisabled }}
336
340
  aria-required={isRequired}
337
341
  aria-invalid={invalid}
338
342
  className={slots.field({ className })}
@@ -0,0 +1,6 @@
1
+ export function textareaIsDisabled(
2
+ disabled: boolean | undefined,
3
+ editable: boolean | undefined
4
+ ): boolean {
5
+ return !!disabled || editable === false;
6
+ }