@delacour/react-native-ui 0.1.0-alpha.4 → 0.2.0-alpha.20261010061731

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 (493) hide show
  1. package/README.md +10 -6
  2. package/package.json +49 -5
  3. package/src/components/accordion/AGENTS.md +21 -9
  4. package/src/components/accordion/accordion-content.tsx +7 -4
  5. package/src/components/accordion/accordion-item.tsx +15 -13
  6. package/src/components/accordion/accordion.context.tsx +11 -5
  7. package/src/components/accordion/accordion.variants.test.ts +64 -3
  8. package/src/components/accordion/accordion.variants.ts +33 -9
  9. package/src/components/alert/AGENTS.md +102 -0
  10. package/src/components/alert/alert-action.tsx +17 -0
  11. package/src/components/alert/alert-close-button.tsx +50 -0
  12. package/src/components/alert/alert-content.tsx +15 -0
  13. package/src/components/alert/alert-description.tsx +16 -0
  14. package/src/components/alert/alert-glyphs.ts +25 -0
  15. package/src/components/alert/alert-indicator.tsx +40 -0
  16. package/src/components/alert/alert-title.tsx +18 -0
  17. package/src/components/alert/alert.context.tsx +69 -0
  18. package/src/components/alert/alert.tsx +160 -0
  19. package/src/components/alert/alert.types.ts +10 -0
  20. package/src/components/alert/alert.variants.test.ts +218 -0
  21. package/src/components/alert/alert.variants.ts +180 -0
  22. package/src/components/alert/index.ts +23 -0
  23. package/src/components/avatar/AGENTS.md +106 -0
  24. package/src/components/avatar/avatar-badge.tsx +51 -0
  25. package/src/components/avatar/avatar-group.tsx +139 -0
  26. package/src/components/avatar/avatar.context.tsx +93 -0
  27. package/src/components/avatar/avatar.tsx +201 -0
  28. package/src/components/avatar/avatar.utils.test.ts +174 -0
  29. package/src/components/avatar/avatar.utils.ts +141 -0
  30. package/src/components/avatar/avatar.variants.test.ts +251 -0
  31. package/src/components/avatar/avatar.variants.ts +270 -0
  32. package/src/components/avatar/index.ts +39 -0
  33. package/src/components/badge/AGENTS.md +10 -0
  34. package/src/components/badge/badge-close-button.tsx +2 -2
  35. package/src/components/badge/badge.context.tsx +2 -1
  36. package/src/components/badge/badge.tsx +13 -4
  37. package/src/components/badge/badge.variants.test.ts +26 -0
  38. package/src/components/badge/badge.variants.ts +21 -0
  39. package/src/components/bottom-sheet/AGENTS.md +186 -231
  40. package/src/components/bottom-sheet/bottom-sheet-background.tsx +31 -22
  41. package/src/components/bottom-sheet/bottom-sheet-close.tsx +23 -30
  42. package/src/components/bottom-sheet/bottom-sheet-container.tsx +54 -208
  43. package/src/components/bottom-sheet/bottom-sheet-content.tsx +25 -48
  44. package/src/components/bottom-sheet/bottom-sheet-description.tsx +8 -2
  45. package/src/components/bottom-sheet/bottom-sheet-flat-list.tsx +65 -0
  46. package/src/components/bottom-sheet/bottom-sheet-footer.tsx +53 -122
  47. package/src/components/bottom-sheet/bottom-sheet-handle.tsx +31 -26
  48. package/src/components/bottom-sheet/bottom-sheet-legend-list.tsx +69 -0
  49. package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +25 -147
  50. package/src/components/bottom-sheet/bottom-sheet-provider.tsx +42 -0
  51. package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +38 -62
  52. package/src/components/bottom-sheet/bottom-sheet-section-list.tsx +58 -0
  53. package/src/components/bottom-sheet/bottom-sheet-steps.tsx +88 -0
  54. package/src/components/bottom-sheet/bottom-sheet-text-input.tsx +44 -0
  55. package/src/components/bottom-sheet/bottom-sheet-title.tsx +12 -8
  56. package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +19 -37
  57. package/src/components/bottom-sheet/bottom-sheet.tsx +113 -60
  58. package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +47 -87
  59. package/src/components/bottom-sheet/bottom-sheet.variants.ts +50 -126
  60. package/src/components/bottom-sheet/index.ts +39 -17
  61. package/src/components/bottom-sheet/use-bottom-sheet-input.ts +16 -115
  62. package/src/components/button/AGENTS.md +51 -11
  63. package/src/components/button/button-group.tsx +44 -8
  64. package/src/components/button/button.context.tsx +10 -0
  65. package/src/components/button/button.tsx +7 -1
  66. package/src/components/button/button.variants.test.ts +190 -0
  67. package/src/components/button/button.variants.ts +87 -6
  68. package/src/components/button/index.ts +2 -0
  69. package/src/components/calendar/AGENTS.md +142 -0
  70. package/src/components/calendar/calendar-caption.tsx +116 -0
  71. package/src/components/calendar/calendar-day.tsx +103 -0
  72. package/src/components/calendar/calendar-grid.tsx +183 -0
  73. package/src/components/calendar/calendar-header.tsx +34 -0
  74. package/src/components/calendar/calendar-nav.tsx +51 -0
  75. package/src/components/calendar/calendar-picker.tsx +128 -0
  76. package/src/components/calendar/calendar-weekdays.tsx +48 -0
  77. package/src/components/calendar/calendar.context.tsx +108 -0
  78. package/src/components/calendar/calendar.date.test.ts +456 -0
  79. package/src/components/calendar/calendar.date.ts +584 -0
  80. package/src/components/calendar/calendar.tsx +389 -0
  81. package/src/components/calendar/calendar.types.ts +87 -0
  82. package/src/components/calendar/calendar.variants.test.ts +363 -0
  83. package/src/components/calendar/calendar.variants.ts +426 -0
  84. package/src/components/calendar/index.ts +96 -0
  85. package/src/components/card/AGENTS.md +92 -0
  86. package/src/components/card/card-action.tsx +16 -0
  87. package/src/components/card/card-content.tsx +12 -0
  88. package/src/components/card/card-description.tsx +12 -0
  89. package/src/components/card/card-footer.tsx +30 -0
  90. package/src/components/card/card-header.tsx +45 -0
  91. package/src/components/card/card-title.tsx +25 -0
  92. package/src/components/card/card.context.tsx +64 -0
  93. package/src/components/card/card.tsx +100 -0
  94. package/src/components/card/card.types.ts +13 -0
  95. package/src/components/card/card.variants.test.ts +240 -0
  96. package/src/components/card/card.variants.ts +177 -0
  97. package/src/components/card/index.ts +15 -0
  98. package/src/components/chart/AGENTS.md +8 -0
  99. package/src/components/chart/chart.tsx +8 -1
  100. package/src/components/chip/AGENTS.md +113 -0
  101. package/src/components/chip/chip-close-button.tsx +48 -0
  102. package/src/components/chip/chip-end-content.tsx +15 -0
  103. package/src/components/chip/chip-label.tsx +21 -0
  104. package/src/components/chip/chip-start-content.tsx +16 -0
  105. package/src/components/chip/chip.context.tsx +66 -0
  106. package/src/components/chip/chip.tsx +268 -0
  107. package/src/components/chip/chip.types.ts +10 -0
  108. package/src/components/chip/chip.variants.test.ts +335 -0
  109. package/src/components/chip/chip.variants.ts +321 -0
  110. package/src/components/chip/index.ts +24 -0
  111. package/src/components/collapsible/AGENTS.md +97 -0
  112. package/src/components/collapsible/collapsible-content.tsx +97 -0
  113. package/src/components/collapsible/collapsible-description.tsx +17 -0
  114. package/src/components/collapsible/collapsible-indicator.tsx +92 -0
  115. package/src/components/collapsible/collapsible-title.tsx +17 -0
  116. package/src/components/collapsible/collapsible-trigger.tsx +133 -0
  117. package/src/components/collapsible/collapsible.context.tsx +91 -0
  118. package/src/components/collapsible/collapsible.tsx +167 -0
  119. package/src/components/collapsible/collapsible.types.ts +9 -0
  120. package/src/components/collapsible/collapsible.variants.test.ts +338 -0
  121. package/src/components/collapsible/collapsible.variants.ts +228 -0
  122. package/src/components/collapsible/index.ts +32 -0
  123. package/src/components/context-menu/AGENTS.md +103 -0
  124. package/src/components/context-menu/context-menu-content.tsx +64 -0
  125. package/src/components/context-menu/context-menu-item.tsx +22 -0
  126. package/src/components/context-menu/context-menu-preview.tsx +60 -0
  127. package/src/components/context-menu/context-menu-trigger.tsx +160 -0
  128. package/src/components/context-menu/context-menu.context.tsx +91 -0
  129. package/src/components/context-menu/context-menu.tsx +90 -0
  130. package/src/components/context-menu/context-menu.variants.test.ts +266 -0
  131. package/src/components/context-menu/context-menu.variants.ts +147 -0
  132. package/src/components/context-menu/index.ts +18 -0
  133. package/src/components/dialog/AGENTS.md +97 -0
  134. package/src/components/dialog/dialog-body.tsx +21 -0
  135. package/src/components/dialog/dialog-close.tsx +82 -0
  136. package/src/components/dialog/dialog-content.tsx +153 -0
  137. package/src/components/dialog/dialog-description.tsx +23 -0
  138. package/src/components/dialog/dialog-footer.tsx +32 -0
  139. package/src/components/dialog/dialog-header.tsx +20 -0
  140. package/src/components/dialog/dialog-title.tsx +42 -0
  141. package/src/components/dialog/dialog-trigger.tsx +54 -0
  142. package/src/components/dialog/dialog.context.tsx +90 -0
  143. package/src/components/dialog/dialog.tsx +119 -0
  144. package/src/components/dialog/dialog.variants.test.ts +224 -0
  145. package/src/components/dialog/dialog.variants.ts +173 -0
  146. package/src/components/dialog/index.ts +30 -0
  147. package/src/components/drawer/AGENTS.md +123 -0
  148. package/src/components/drawer/drawer-body.tsx +46 -0
  149. package/src/components/drawer/drawer-close.tsx +76 -0
  150. package/src/components/drawer/drawer-content.tsx +186 -0
  151. package/src/components/drawer/drawer-description.tsx +23 -0
  152. package/src/components/drawer/drawer-footer.tsx +21 -0
  153. package/src/components/drawer/drawer-header.tsx +42 -0
  154. package/src/components/drawer/drawer-title.tsx +35 -0
  155. package/src/components/drawer/drawer-trigger.tsx +54 -0
  156. package/src/components/drawer/drawer.context.tsx +114 -0
  157. package/src/components/drawer/drawer.tsx +119 -0
  158. package/src/components/drawer/drawer.variants.test.ts +392 -0
  159. package/src/components/drawer/drawer.variants.ts +244 -0
  160. package/src/components/drawer/index.ts +41 -0
  161. package/src/components/drawer/use-drawer-pan.ts +108 -0
  162. package/src/components/empty-state/AGENTS.md +90 -0
  163. package/src/components/empty-state/empty-state-content.tsx +17 -0
  164. package/src/components/empty-state/empty-state-description.tsx +12 -0
  165. package/src/components/empty-state/empty-state-header.tsx +17 -0
  166. package/src/components/empty-state/empty-state-media.tsx +56 -0
  167. package/src/components/empty-state/empty-state-title.tsx +18 -0
  168. package/src/components/empty-state/empty-state.context.tsx +65 -0
  169. package/src/components/empty-state/empty-state.tsx +84 -0
  170. package/src/components/empty-state/empty-state.types.ts +14 -0
  171. package/src/components/empty-state/empty-state.variants.test.ts +245 -0
  172. package/src/components/empty-state/empty-state.variants.ts +132 -0
  173. package/src/components/empty-state/index.ts +20 -0
  174. package/src/components/fab/AGENTS.md +119 -0
  175. package/src/components/fab/fab-action.tsx +118 -0
  176. package/src/components/fab/fab-group.tsx +214 -0
  177. package/src/components/fab/fab-label.tsx +18 -0
  178. package/src/components/fab/fab.context.tsx +125 -0
  179. package/src/components/fab/fab.tsx +156 -0
  180. package/src/components/fab/fab.types.ts +17 -0
  181. package/src/components/fab/fab.variants.test.ts +274 -0
  182. package/src/components/fab/fab.variants.ts +234 -0
  183. package/src/components/fab/index.ts +37 -0
  184. package/src/components/feedback/AGENTS.md +95 -0
  185. package/src/components/feedback/feedback-action.tsx +16 -0
  186. package/src/components/feedback/feedback-cancel.tsx +34 -0
  187. package/src/components/feedback/feedback-close.tsx +21 -0
  188. package/src/components/feedback/feedback-content.tsx +24 -0
  189. package/src/components/feedback/feedback-field.tsx +70 -0
  190. package/src/components/feedback/feedback-footer.tsx +21 -0
  191. package/src/components/feedback/feedback-panel.tsx +54 -0
  192. package/src/components/feedback/feedback-submit.tsx +74 -0
  193. package/src/components/feedback/feedback-title.tsx +34 -0
  194. package/src/components/feedback/feedback-trigger.tsx +20 -0
  195. package/src/components/feedback/feedback.context.tsx +73 -0
  196. package/src/components/feedback/feedback.tsx +135 -0
  197. package/src/components/feedback/feedback.variants.test.ts +185 -0
  198. package/src/components/feedback/feedback.variants.ts +128 -0
  199. package/src/components/feedback/index.ts +24 -0
  200. package/src/components/field/AGENTS.md +19 -0
  201. package/src/components/field/field-label.tsx +19 -5
  202. package/src/components/field/field.context.tsx +17 -0
  203. package/src/components/field/field.tsx +15 -2
  204. package/src/components/field/field.variants.test.ts +24 -0
  205. package/src/components/field/field.variants.ts +20 -0
  206. package/src/components/field/index.ts +1 -0
  207. package/src/components/input/AGENTS.md +5 -0
  208. package/src/components/input/input.variants.test.ts +32 -0
  209. package/src/components/input/input.variants.ts +13 -1
  210. package/src/components/item/AGENTS.md +92 -0
  211. package/src/components/item/index.ts +25 -0
  212. package/src/components/item/item-actions.tsx +28 -0
  213. package/src/components/item/item-content.tsx +15 -0
  214. package/src/components/item/item-description.tsx +12 -0
  215. package/src/components/item/item-footer.tsx +13 -0
  216. package/src/components/item/item-group.tsx +32 -0
  217. package/src/components/item/item-header.tsx +13 -0
  218. package/src/components/item/item-media.tsx +37 -0
  219. package/src/components/item/item-separator.tsx +13 -0
  220. package/src/components/item/item-title.tsx +17 -0
  221. package/src/components/item/item.context.tsx +66 -0
  222. package/src/components/item/item.tsx +218 -0
  223. package/src/components/item/item.types.ts +12 -0
  224. package/src/components/item/item.variants.test.ts +263 -0
  225. package/src/components/item/item.variants.ts +200 -0
  226. package/src/components/kpi/AGENTS.md +125 -0
  227. package/src/components/kpi/index.ts +45 -0
  228. package/src/components/kpi/kpi-action.tsx +12 -0
  229. package/src/components/kpi/kpi-content.tsx +35 -0
  230. package/src/components/kpi/kpi-footer.tsx +52 -0
  231. package/src/components/kpi/kpi-group.tsx +70 -0
  232. package/src/components/kpi/kpi-header.tsx +25 -0
  233. package/src/components/kpi/kpi-icon.tsx +28 -0
  234. package/src/components/kpi/kpi-sparkline.tsx +145 -0
  235. package/src/components/kpi/kpi-stat.tsx +20 -0
  236. package/src/components/kpi/kpi-title.tsx +19 -0
  237. package/src/components/kpi/kpi-trend.tsx +105 -0
  238. package/src/components/kpi/kpi-value.tsx +32 -0
  239. package/src/components/kpi/kpi.context.tsx +111 -0
  240. package/src/components/kpi/kpi.tsx +154 -0
  241. package/src/components/kpi/kpi.types.ts +9 -0
  242. package/src/components/kpi/kpi.variants.test.ts +428 -0
  243. package/src/components/kpi/kpi.variants.ts +347 -0
  244. package/src/components/label/AGENTS.md +89 -0
  245. package/src/components/label/index.ts +10 -0
  246. package/src/components/label/label.tsx +97 -0
  247. package/src/components/label/label.variants.test.ts +176 -0
  248. package/src/components/label/label.variants.ts +123 -0
  249. package/src/components/menu/AGENTS.md +176 -0
  250. package/src/components/menu/index.ts +44 -0
  251. package/src/components/menu/menu-background.tsx +17 -0
  252. package/src/components/menu/menu-checkbox-item.tsx +57 -0
  253. package/src/components/menu/menu-content.tsx +303 -0
  254. package/src/components/menu/menu-item.tsx +162 -0
  255. package/src/components/menu/menu-label.tsx +14 -0
  256. package/src/components/menu/menu-radio-group.tsx +50 -0
  257. package/src/components/menu/menu-radio-item.tsx +57 -0
  258. package/src/components/menu/menu-separator.tsx +11 -0
  259. package/src/components/menu/menu-sub-content.tsx +72 -0
  260. package/src/components/menu/menu-sub-trigger.tsx +50 -0
  261. package/src/components/menu/menu-sub.tsx +75 -0
  262. package/src/components/menu/menu-trigger.tsx +57 -0
  263. package/src/components/menu/menu.context.tsx +214 -0
  264. package/src/components/menu/menu.tsx +88 -0
  265. package/src/components/menu/menu.types.ts +33 -0
  266. package/src/components/menu/menu.variants.test.ts +400 -0
  267. package/src/components/menu/menu.variants.ts +353 -0
  268. package/src/components/meter/AGENTS.md +135 -0
  269. package/src/components/meter/index.ts +43 -0
  270. package/src/components/meter/meter-fill.tsx +19 -0
  271. package/src/components/meter/meter-header.tsx +17 -0
  272. package/src/components/meter/meter-label.tsx +18 -0
  273. package/src/components/meter/meter-output.tsx +42 -0
  274. package/src/components/meter/meter-segments.tsx +83 -0
  275. package/src/components/meter/meter-track.tsx +16 -0
  276. package/src/components/meter/meter.context.tsx +62 -0
  277. package/src/components/meter/meter.tsx +286 -0
  278. package/src/components/meter/meter.types.ts +30 -0
  279. package/src/components/meter/meter.variants.test.ts +430 -0
  280. package/src/components/meter/meter.variants.ts +347 -0
  281. package/src/components/overlay/AGENTS.md +89 -0
  282. package/src/components/overlay/index.ts +27 -0
  283. package/src/components/overlay/overlay-portal.tsx +84 -0
  284. package/src/components/overlay/overlay-presence.test.ts +46 -0
  285. package/src/components/overlay/overlay-presence.ts +49 -0
  286. package/src/components/overlay/overlay-registry.test.ts +96 -0
  287. package/src/components/overlay/overlay-registry.ts +99 -0
  288. package/src/components/overlay/overlay-scrim.tsx +49 -0
  289. package/src/components/overlay/overlay.context.tsx +48 -0
  290. package/src/components/overlay/overlay.tsx +118 -0
  291. package/src/components/overlay/overlay.variants.test.ts +37 -0
  292. package/src/components/overlay/overlay.variants.ts +33 -0
  293. package/src/components/overlay/use-overlay-back-handler.ts +34 -0
  294. package/src/components/overlay/use-overlay-presence.ts +87 -0
  295. package/src/components/popover/AGENTS.md +138 -0
  296. package/src/components/popover/index.ts +48 -0
  297. package/src/components/popover/popover-anchor.tsx +49 -0
  298. package/src/components/popover/popover-arrow.tsx +89 -0
  299. package/src/components/popover/popover-close.tsx +73 -0
  300. package/src/components/popover/popover-content.tsx +235 -0
  301. package/src/components/popover/popover-description.tsx +18 -0
  302. package/src/components/popover/popover-title.tsx +33 -0
  303. package/src/components/popover/popover-trigger.tsx +56 -0
  304. package/src/components/popover/popover.context.tsx +71 -0
  305. package/src/components/popover/popover.position.test.ts +354 -0
  306. package/src/components/popover/popover.position.ts +286 -0
  307. package/src/components/popover/popover.tsx +123 -0
  308. package/src/components/popover/popover.variants.test.ts +135 -0
  309. package/src/components/popover/popover.variants.ts +85 -0
  310. package/src/components/popover/use-anchor-measure.ts +76 -0
  311. package/src/components/popover/use-anchored-content.ts +219 -0
  312. package/src/components/pressable/AGENTS.md +11 -1
  313. package/src/components/pressable/pressable.tsx +7 -2
  314. package/src/components/pressable/pressable.variants.test.ts +29 -0
  315. package/src/components/pressable/pressable.variants.ts +17 -0
  316. package/src/components/progress/AGENTS.md +120 -0
  317. package/src/components/progress/index.ts +40 -0
  318. package/src/components/progress/progress-fill.tsx +152 -0
  319. package/src/components/progress/progress-header.tsx +19 -0
  320. package/src/components/progress/progress-label.tsx +27 -0
  321. package/src/components/progress/progress-output.tsx +47 -0
  322. package/src/components/progress/progress-track.tsx +38 -0
  323. package/src/components/progress/progress.context.tsx +79 -0
  324. package/src/components/progress/progress.tsx +171 -0
  325. package/src/components/progress/progress.types.ts +23 -0
  326. package/src/components/progress/progress.variants.test.ts +335 -0
  327. package/src/components/progress/progress.variants.ts +284 -0
  328. package/src/components/progress-button/AGENTS.md +116 -0
  329. package/src/components/progress-button/index.ts +36 -0
  330. package/src/components/progress-button/progress-button-done.tsx +33 -0
  331. package/src/components/progress-button/progress-button-label.tsx +27 -0
  332. package/src/components/progress-button/progress-button.context.tsx +93 -0
  333. package/src/components/progress-button/progress-button.tsx +414 -0
  334. package/src/components/progress-button/progress-button.variants.test.ts +314 -0
  335. package/src/components/progress-button/progress-button.variants.ts +236 -0
  336. package/src/components/provider/AGENTS.md +38 -25
  337. package/src/components/provider/provider.tsx +38 -11
  338. package/src/components/radio/AGENTS.md +8 -10
  339. package/src/components/radio/radio.variants.test.ts +5 -6
  340. package/src/components/radio/radio.variants.ts +6 -7
  341. package/src/components/rating/AGENTS.md +143 -0
  342. package/src/components/rating/index.ts +43 -0
  343. package/src/components/rating/rating-output.tsx +39 -0
  344. package/src/components/rating/rating-star.tsx +70 -0
  345. package/src/components/rating/rating-stars.tsx +219 -0
  346. package/src/components/rating/rating.context.tsx +79 -0
  347. package/src/components/rating/rating.tsx +163 -0
  348. package/src/components/rating/rating.variants.test.ts +347 -0
  349. package/src/components/rating/rating.variants.ts +354 -0
  350. package/src/components/selection-mode/AGENTS.md +155 -0
  351. package/src/components/selection-mode/index.ts +43 -0
  352. package/src/components/selection-mode/selection-mode-action.tsx +65 -0
  353. package/src/components/selection-mode/selection-mode-bar.tsx +79 -0
  354. package/src/components/selection-mode/selection-mode-group.tsx +123 -0
  355. package/src/components/selection-mode/selection-mode-header.tsx +85 -0
  356. package/src/components/selection-mode/selection-mode-indicator.tsx +118 -0
  357. package/src/components/selection-mode/selection-mode-item.tsx +182 -0
  358. package/src/components/selection-mode/selection-mode.context.tsx +89 -0
  359. package/src/components/selection-mode/selection-mode.tsx +207 -0
  360. package/src/components/selection-mode/selection-mode.types.ts +36 -0
  361. package/src/components/selection-mode/selection-mode.variants.test.ts +292 -0
  362. package/src/components/selection-mode/selection-mode.variants.ts +329 -0
  363. package/src/components/skeleton/AGENTS.md +120 -0
  364. package/src/components/skeleton/index.ts +15 -0
  365. package/src/components/skeleton/skeleton-clock.ts +48 -0
  366. package/src/components/skeleton/skeleton-group.tsx +64 -0
  367. package/src/components/skeleton/skeleton-shimmer.tsx +80 -0
  368. package/src/components/skeleton/skeleton.context.tsx +45 -0
  369. package/src/components/skeleton/skeleton.tsx +227 -0
  370. package/src/components/skeleton/skeleton.variants.test.ts +245 -0
  371. package/src/components/skeleton/skeleton.variants.ts +222 -0
  372. package/src/components/slide-button/AGENTS.md +122 -0
  373. package/src/components/slide-button/index.ts +43 -0
  374. package/src/components/slide-button/slide-button-label.tsx +70 -0
  375. package/src/components/slide-button/slide-button-thumb.tsx +93 -0
  376. package/src/components/slide-button/slide-button.context.tsx +76 -0
  377. package/src/components/slide-button/slide-button.tsx +457 -0
  378. package/src/components/slide-button/slide-button.variants.test.ts +288 -0
  379. package/src/components/slide-button/slide-button.variants.ts +324 -0
  380. package/src/components/slider/index.ts +2 -0
  381. package/src/components/slider/slider-thumb.tsx +33 -1
  382. package/src/components/slider/slider.variants.test.ts +52 -0
  383. package/src/components/slider/slider.variants.ts +56 -2
  384. package/src/components/stack-card/AGENTS.md +169 -0
  385. package/src/components/stack-card/index.ts +37 -0
  386. package/src/components/stack-card/stack-card-action.tsx +66 -0
  387. package/src/components/stack-card/stack-card-actions.tsx +18 -0
  388. package/src/components/stack-card/stack-card-card.tsx +71 -0
  389. package/src/components/stack-card/stack-card-empty.tsx +30 -0
  390. package/src/components/stack-card/stack-card-slot.tsx +90 -0
  391. package/src/components/stack-card/stack-card-stamp.tsx +76 -0
  392. package/src/components/stack-card/stack-card.context.tsx +117 -0
  393. package/src/components/stack-card/stack-card.tsx +557 -0
  394. package/src/components/stack-card/stack-card.types.ts +26 -0
  395. package/src/components/stack-card/stack-card.variants.test.ts +355 -0
  396. package/src/components/stack-card/stack-card.variants.ts +360 -0
  397. package/src/components/steps/AGENTS.md +153 -0
  398. package/src/components/steps/index.ts +45 -0
  399. package/src/components/steps/steps-connector.tsx +31 -0
  400. package/src/components/steps/steps-description.tsx +26 -0
  401. package/src/components/steps/steps-indicator.tsx +106 -0
  402. package/src/components/steps/steps-item.tsx +225 -0
  403. package/src/components/steps/steps-panel.tsx +41 -0
  404. package/src/components/steps/steps-title.tsx +32 -0
  405. package/src/components/steps/steps-track.tsx +59 -0
  406. package/src/components/steps/steps.context.tsx +123 -0
  407. package/src/components/steps/steps.tsx +166 -0
  408. package/src/components/steps/steps.variants.test.ts +388 -0
  409. package/src/components/steps/steps.variants.ts +468 -0
  410. package/src/components/surface/AGENTS.md +82 -0
  411. package/src/components/surface/index.ts +15 -0
  412. package/src/components/surface/surface.context.tsx +56 -0
  413. package/src/components/surface/surface.tsx +87 -0
  414. package/src/components/surface/surface.variants.test.ts +227 -0
  415. package/src/components/surface/surface.variants.ts +165 -0
  416. package/src/components/swipe/AGENTS.md +140 -0
  417. package/src/components/swipe/index.ts +31 -0
  418. package/src/components/swipe/swipe-action.tsx +99 -0
  419. package/src/components/swipe/swipe-end.tsx +14 -0
  420. package/src/components/swipe/swipe-group.tsx +55 -0
  421. package/src/components/swipe/swipe-panel.tsx +55 -0
  422. package/src/components/swipe/swipe-start.tsx +15 -0
  423. package/src/components/swipe/swipe.context.tsx +135 -0
  424. package/src/components/swipe/swipe.tsx +393 -0
  425. package/src/components/swipe/swipe.types.ts +7 -0
  426. package/src/components/swipe/swipe.variants.test.ts +337 -0
  427. package/src/components/swipe/swipe.variants.ts +290 -0
  428. package/src/components/switch/switch.variants.ts +2 -2
  429. package/src/components/tabs/AGENTS.md +4 -3
  430. package/src/components/tabs/tabs.variants.test.ts +1 -1
  431. package/src/components/tabs/tabs.variants.ts +4 -6
  432. package/src/components/text/AGENTS.md +8 -2
  433. package/src/components/text/text.tsx +7 -0
  434. package/src/components/text/text.variants.test.ts +24 -1
  435. package/src/components/text/text.variants.ts +5 -0
  436. package/src/components/textarea/AGENTS.md +89 -0
  437. package/src/components/textarea/index.ts +16 -0
  438. package/src/components/textarea/textarea.tsx +157 -0
  439. package/src/components/textarea/textarea.variants.test.ts +219 -0
  440. package/src/components/textarea/textarea.variants.ts +171 -0
  441. package/src/components/toast/AGENTS.md +140 -0
  442. package/src/components/toast/index.ts +74 -0
  443. package/src/components/toast/toast-action.tsx +59 -0
  444. package/src/components/toast/toast-api.ts +32 -0
  445. package/src/components/toast/toast-close.tsx +44 -0
  446. package/src/components/toast/toast-content.tsx +11 -0
  447. package/src/components/toast/toast-description.tsx +11 -0
  448. package/src/components/toast/toast-indicator.tsx +34 -0
  449. package/src/components/toast/toast-item.tsx +308 -0
  450. package/src/components/toast/toast-title.tsx +13 -0
  451. package/src/components/toast/toast-viewport.tsx +135 -0
  452. package/src/components/toast/toast.context.tsx +69 -0
  453. package/src/components/toast/toast.store.test.ts +259 -0
  454. package/src/components/toast/toast.store.ts +338 -0
  455. package/src/components/toast/toast.timer.test.ts +64 -0
  456. package/src/components/toast/toast.timer.ts +51 -0
  457. package/src/components/toast/toast.tsx +132 -0
  458. package/src/components/toast/toast.variants.test.ts +393 -0
  459. package/src/components/toast/toast.variants.ts +304 -0
  460. package/src/components/toggle-button/AGENTS.md +106 -0
  461. package/src/components/toggle-button/index.ts +30 -0
  462. package/src/components/toggle-button/toggle-button-group.tsx +156 -0
  463. package/src/components/toggle-button/toggle-button-label.tsx +19 -0
  464. package/src/components/toggle-button/toggle-button.context.tsx +130 -0
  465. package/src/components/toggle-button/toggle-button.tsx +172 -0
  466. package/src/components/toggle-button/toggle-button.variants.test.ts +189 -0
  467. package/src/components/toggle-button/toggle-button.variants.ts +160 -0
  468. package/src/components/tooltip/AGENTS.md +133 -0
  469. package/src/components/tooltip/index.ts +28 -0
  470. package/src/components/tooltip/tooltip-arrow.tsx +40 -0
  471. package/src/components/tooltip/tooltip-content.tsx +179 -0
  472. package/src/components/tooltip/tooltip-description.tsx +19 -0
  473. package/src/components/tooltip/tooltip-text.tsx +21 -0
  474. package/src/components/tooltip/tooltip-title.tsx +20 -0
  475. package/src/components/tooltip/tooltip-trigger.tsx +103 -0
  476. package/src/components/tooltip/tooltip.context.tsx +68 -0
  477. package/src/components/tooltip/tooltip.tsx +213 -0
  478. package/src/components/tooltip/tooltip.variants.test.ts +231 -0
  479. package/src/components/tooltip/tooltip.variants.ts +177 -0
  480. package/src/hooks/use-calm-motion.tsx +31 -0
  481. package/src/lib/calm-motion.test.ts +17 -0
  482. package/src/lib/calm-motion.ts +23 -0
  483. package/src/lib/cn.parity.test.ts +62 -0
  484. package/src/lib/cn.test.ts +14 -0
  485. package/src/lib/cn.ts +11 -6
  486. package/src/no-gorhom.test.ts +54 -0
  487. package/src/styles/geometry.test.ts +1 -0
  488. package/src/styles/theme.css +4 -0
  489. package/src/styles/tokens.css +44 -0
  490. package/src/styles/tokens.test.ts +56 -0
  491. package/src/styles/tokens.ts +29 -1
  492. package/src/components/bottom-sheet/bottom-sheet-portal.tsx +0 -72
  493. package/src/components/bottom-sheet/bottom-sheet.context.tsx +0 -135
@@ -13,45 +13,23 @@ export const BOTTOM_SHEET_OVERLAY_TOKEN = "overlay";
13
13
  /**
14
14
  * The backdrop's opacity at full appearance.
15
15
  *
16
- * **1, not gorhom's 0.5.** The alpha lives in `--overlay`, so leaving the
17
- * library default in place would multiply the two and land the scrim at roughly
18
- * a fifth of what the theme asked for. One source for the alpha, and it is the
19
- * token — which is also what lets the two theme variants differ.
16
+ * **1.** The alpha lives in `--overlay`, so any other number here would multiply
17
+ * against it and land the scrim somewhere the theme did not ask for. One source
18
+ * for the alpha, and it is the token — which is also what lets the two theme
19
+ * variants differ.
20
20
  */
21
21
  export const BOTTOM_SHEET_OVERLAY_OPACITY = 1;
22
22
 
23
23
  /**
24
- * The snap indices the scrim appears and disappears on.
24
+ * The snap point indices the scrim appears and disappears on.
25
25
  *
26
- * gorhom defaults to `{ appearsOnIndex: 1, disappearsOnIndex: 0 }`, which suits
27
- * a persistent sheet that rests collapsed on the screen and only dims the app
28
- * once it is expanded. A modal sheet has no resting state: it is either
29
- * presented or gone, so the scrim belongs from the FIRST snap point (`0`) and
30
- * is only absent when the sheet is closed (`-1`).
26
+ * A modal sheet has no resting state: it is either presented or gone, so the
27
+ * scrim belongs from the FIRST snap point (`0`) and is only absent when the sheet
28
+ * is closed (`-1`). These are the engine's own defaults too; they are named
29
+ * here so the test that pins them has something to read.
31
30
  */
32
31
  export const BOTTOM_SHEET_BACKDROP_INDICES = { appearsOnIndex: 0, disappearsOnIndex: -1 } as const;
33
32
 
34
- /**
35
- * Keyboard behaviour every `BottomSheet.Container` starts from.
36
- *
37
- * A const rather than three defaults spelled inline, so `bun test` pins them and
38
- * a retune is one edit rather than three.
39
- *
40
- * - `interactive` — the sheet grows to keep its content above the keyboard,
41
- * following it frame by frame rather than snapping once it has settled.
42
- * - `restore` — blurring returns the sheet to the snap point it was on. Without
43
- * it a sheet that grew for the keyboard stays grown over empty screen.
44
- * - `adjustResize` — gorhom's own default is `adjustPan`, which leaves the
45
- * Android window height alone and slides the whole view up, so a sheet's
46
- * footer lands off-screen. `resize` is already what Expo configures and what
47
- * `KeyboardProvider` requires, so this is the value that matches the window.
48
- */
49
- export const BOTTOM_SHEET_KEYBOARD_DEFAULTS = {
50
- keyboardBehavior: "interactive",
51
- keyboardBlurBehavior: "restore",
52
- android_keyboardInputMode: "adjustResize",
53
- } as const;
54
-
55
33
  /**
56
34
  * Slop around `BottomSheet.Close`.
57
35
  *
@@ -62,22 +40,23 @@ export const BOTTOM_SHEET_KEYBOARD_DEFAULTS = {
62
40
  export const BOTTOM_SHEET_CLOSE_HIT_SLOP = 8;
63
41
 
64
42
  /**
65
- * The padding a pinned footer keeps whatever the keyboard is doing.
43
+ * The padding a pinned footer's measured box carries.
66
44
  *
67
- * A number rather than a `p-4`, and for `SCREEN_FOOTER_PADDING`'s reason: the
68
- * footer's bottom padding is an animated value — the safe-area band collapses
69
- * into it as the keyboard arrives — and a class cannot carry one. Its top
70
- * padding is written the same way so the two cannot drift.
45
+ * A number rather than a `p-4`, because it is handed to the engine's `padding`
46
+ * prop: the engine measures the footer's inner box into the dynamic snap point,
47
+ * and padding written there is counted where a class on the outer view would
48
+ * not be. The horizontal gutter still comes from the `stickyFooter` slot, which
49
+ * is merged after it.
71
50
  */
72
51
  export const BOTTOM_SHEET_FOOTER_PADDING = 16;
73
52
 
74
53
  /**
75
54
  * The gap between the last of the content and a pinned footer.
76
55
  *
77
- * Also a number, because the reserve it belongs to is computed at runtime from
78
- * the footer's measured height. Without it the last row of a list sits flush
79
- * against the footer's hairline, which reads as content clipped rather than
80
- * content ended.
56
+ * Also a number, because it is the engine's `footerGap` — added to the spacer it
57
+ * reserves under the body from the footer's measured height. Without it the last
58
+ * row of a list sits flush against the footer's hairline, which reads as content
59
+ * clipped rather than content ended.
81
60
  */
82
61
  export const BOTTOM_SHEET_FOOTER_GAP = 16;
83
62
 
@@ -85,7 +64,7 @@ export const bottomSheetVariants = tv({
85
64
  slots: {
86
65
  /**
87
66
  * The scrim. Carries the colour and nothing else — the fade is an animated
88
- * opacity gorhom drives off the sheet's own position.
67
+ * opacity the engine drives off the sheet's own index.
89
68
  */
90
69
  overlay: "bg-overlay",
91
70
  /**
@@ -94,7 +73,8 @@ export const bottomSheetVariants = tv({
94
73
  * surface token nothing else in the package had claimed.
95
74
  *
96
75
  * Only the top corners round. The bottom edge runs off the screen, and a
97
- * radius there shows as two notches of the app behind it.
76
+ * radius there shows as two notches of the app behind it. The `detached`
77
+ * variant is the exception — a floating card has a visible bottom edge.
98
78
  */
99
79
  background: "rounded-t-2xl bg-popover",
100
80
  handle: "items-center justify-center pt-3 pb-1",
@@ -102,6 +82,14 @@ export const bottomSheetVariants = tv({
102
82
  content: "gap-4 px-screen-gutter pt-2",
103
83
  /** A scrollable body's content container. Same treatment as `content`. */
104
84
  scrollContent: "gap-4 px-screen-gutter pt-2",
85
+ /**
86
+ * A multi-step body. The gutter only: the engine measures each `Step`, not
87
+ * the box around the stack, so vertical padding here would be height the
88
+ * sheet never counts.
89
+ */
90
+ steps: "px-screen-gutter",
91
+ /** One step of a multi-step body — `content` less the gutter `steps` already carries. */
92
+ step: "gap-4 pt-2",
105
93
  footer: "gap-3 px-screen-gutter pt-4",
106
94
  /**
107
95
  * A pinned footer draws OVER the content, so unlike the inline one it has
@@ -110,9 +98,8 @@ export const bottomSheetVariants = tv({
110
98
  * to overlap blank space. The same reason `Screen.Footer`'s backing lives
111
99
  * inside its sticky view.
112
100
  *
113
- * It carries no vertical padding: that is an animated style, because the
114
- * safe-area band inside it collapses as the keyboard arrives. See
115
- * {@link BOTTOM_SHEET_FOOTER_PADDING}.
101
+ * It carries no vertical padding: that is the engine's `padding` prop, so
102
+ * the measured box counts it. See {@link BOTTOM_SHEET_FOOTER_PADDING}.
116
103
  */
117
104
  stickyFooter: "gap-3 border-border border-t bg-popover px-screen-gutter",
118
105
  close: "absolute top-4 right-4 z-10",
@@ -131,90 +118,27 @@ export const bottomSheetVariants = tv({
131
118
  */
132
119
  title: "pr-8",
133
120
  },
121
+ variants: {
122
+ /**
123
+ * A floating card. Every corner is on screen, so every corner rounds;
124
+ * `rounded-2xl` replaces `rounded-t-2xl` through tailwind-merge rather
125
+ * than stacking beside it.
126
+ */
127
+ detached: {
128
+ true: {
129
+ background: "rounded-2xl",
130
+ },
131
+ },
132
+ },
134
133
  });
135
134
 
136
135
  /**
137
- * There is no `description` or `scrollView` slot.
136
+ * There is no `description`, `panel`, `portal` or `scrollView` slot.
138
137
  *
139
- * Neither has any layout of its own — a description is a `Text.Paragraph` in a
140
- * gap column and a scroll view fills whatever it is given — and `tv` emits
141
- * `undefined` for a slot whose class string is empty, so a slot that says
142
- * nothing is a slot no test can assert against. Those two parts merge the
143
- * caller's `className` with `cn()` instead.
138
+ * None has any layout of its own — a description is a `Text.Paragraph` in a
139
+ * gap column, the panel is a transparent frame the engine positions, a scroll
140
+ * view fills whatever it is given — and `tv` emits `undefined` for a slot whose
141
+ * class string is empty, so a slot that says nothing is a slot no test can
142
+ * assert against. Those parts merge the caller's `className` with `cn()` instead.
144
143
  */
145
144
  export type BottomSheetVariantProps = VariantProps<typeof bottomSheetVariants>;
146
-
147
- /** Where a `BottomSheet.Footer` among a container's children has to be rendered. */
148
- export type BottomSheetFooterPlacement = "sticky" | "inline" | "none";
149
-
150
- /** One child of a `BottomSheet.Container`, reduced to what the placement turns on. */
151
- export type BottomSheetFooterFlag = {
152
- isFooter: boolean;
153
- isSticky: boolean;
154
- };
155
-
156
- /**
157
- * Whether a container's children hold a footer, and where it has to go.
158
- *
159
- * gorhom takes a footer as a `footerComponent` render prop rather than as a
160
- * child, so a sticky one has to be lifted out of the tree it was written in and
161
- * handed over; an inline one simply stays put. This is the decision, kept pure
162
- * so `bun test` reaches the whole matrix — the walk over `Children.toArray` that
163
- * produces the flags lives with its caller, the way `withIndicator` does in
164
- * `radio.tsx`.
165
- *
166
- * A single sticky footer anywhere in the children wins, because two footers is
167
- * not a state worth expressing and the pinned one is the one a caller meant.
168
- */
169
- export function resolveFooterPlacement(flags: readonly BottomSheetFooterFlag[]): BottomSheetFooterPlacement {
170
- let hasFooter = false;
171
-
172
- for (const flag of flags) {
173
- if (!flag.isFooter) continue;
174
- if (flag.isSticky) return "sticky";
175
- hasFooter = true;
176
- }
177
-
178
- return hasFooter ? "inline" : "none";
179
- }
180
-
181
- /**
182
- * The safe-area band `BottomSheet.Content` and `BottomSheet.ScrollView` reserve.
183
- *
184
- * Exactly one thing in the sheet pays for the home indicator. A pinned footer is
185
- * the bottom-most thing there is, and its own box carries the band so its surface
186
- * reaches the bottom of the screen — gorhom then adds that whole measured height
187
- * to the content's reserve, so the content asking for the band as well would
188
- * count it twice and leave a gap the height of the indicator above the footer.
189
- * With no pinned footer the content IS the bottom-most thing and takes it.
190
- */
191
- export function resolveSheetBottomInset({
192
- hasStickyFooter,
193
- bottom,
194
- }: {
195
- hasStickyFooter: boolean;
196
- bottom: number;
197
- }): number {
198
- return hasStickyFooter ? 0 : bottom;
199
- }
200
-
201
- /**
202
- * The padding at the end of `BottomSheet.ScrollView`'s content.
203
- *
204
- * A scroll view does not reserve a pinned footer the way `BottomSheet.Content`
205
- * does — its FRAME stops at the footer's top edge instead, so the scroll
206
- * indicator and the overscroll bounce end there too rather than running on
207
- * underneath it. That leaves the content only the gap to hold it off the
208
- * footer's hairline. With no pinned footer the content is the bottom-most thing
209
- * in the sheet and takes the safe-area band, as {@link resolveSheetBottomInset}
210
- * has it.
211
- */
212
- export function resolveSheetScrollEndPadding({
213
- hasStickyFooter,
214
- bottom,
215
- }: {
216
- hasStickyFooter: boolean;
217
- bottom: number;
218
- }): number {
219
- return hasStickyFooter ? BOTTOM_SHEET_FOOTER_GAP : resolveSheetBottomInset({ bottom, hasStickyFooter });
220
- }
@@ -1,40 +1,62 @@
1
- export { BottomSheet, type BottomSheetProps } from "./bottom-sheet";
2
1
  export {
3
- type BottomSheetContainerContextValue,
4
- BottomSheetContainerProvider,
2
+ type BottomSheetAnimatedValue,
5
3
  type BottomSheetContextValue,
6
- type BottomSheetHandle,
7
- type BottomSheetPortalContextValue,
8
- BottomSheetPortalProvider,
9
- BottomSheetProvider,
4
+ type BottomSheetHostProps,
5
+ type BottomSheetPortalProps,
6
+ type BottomSheetRef,
7
+ type BottomSheetRegistryValue,
8
+ type BottomSheetTextInputHandlers,
9
+ defineSheetMachine,
10
+ type SheetEvent,
11
+ type SheetMachine,
12
+ type SheetMachineConfig,
13
+ type SheetMachineSnapshot,
14
+ type SheetStepController,
15
+ type SheetStepTransition,
10
16
  useBottomSheet,
11
- useBottomSheetContainerContext,
12
- useBottomSheetContext,
13
- useBottomSheetPortalContext,
14
- } from "./bottom-sheet.context";
17
+ useBottomSheetAnimated,
18
+ useBottomSheetRegistry,
19
+ useBottomSheetTextInput,
20
+ useOptionalBottomSheet,
21
+ useOptionalBottomSheet as useBottomSheetContext,
22
+ useSheetMachine,
23
+ useSheetStep,
24
+ } from "@delacour/react-native-bottom-sheet";
25
+ export { resolveSheetBottomInset, resolveSheetScrollEndPadding } from "@delacour/react-native-bottom-sheet/core";
26
+ export { BottomSheet, type BottomSheetProps } from "./bottom-sheet";
15
27
  export {
16
28
  BOTTOM_SHEET_BACKDROP_INDICES,
17
29
  BOTTOM_SHEET_CLOSE_HIT_SLOP,
18
30
  BOTTOM_SHEET_FOOTER_GAP,
19
31
  BOTTOM_SHEET_FOOTER_PADDING,
20
- BOTTOM_SHEET_KEYBOARD_DEFAULTS,
21
32
  BOTTOM_SHEET_OVERLAY_OPACITY,
22
33
  BOTTOM_SHEET_OVERLAY_TOKEN,
23
- type BottomSheetFooterFlag,
24
- type BottomSheetFooterPlacement,
25
34
  type BottomSheetVariantProps,
26
35
  bottomSheetVariants,
27
- resolveFooterPlacement,
28
- resolveSheetBottomInset,
29
36
  } from "./bottom-sheet.variants";
37
+ export type { BottomSheetBackgroundProps } from "./bottom-sheet-background";
30
38
  export type { BottomSheetCloseProps } from "./bottom-sheet-close";
31
39
  export type { BottomSheetContainerProps } from "./bottom-sheet-container";
32
40
  export type { BottomSheetContentProps } from "./bottom-sheet-content";
33
41
  export type { BottomSheetDescriptionProps } from "./bottom-sheet-description";
42
+ export type { BottomSheetFlatListProps } from "./bottom-sheet-flat-list";
34
43
  export type { BottomSheetFooterProps } from "./bottom-sheet-footer";
44
+ export type { BottomSheetHandleProps } from "./bottom-sheet-handle";
45
+ export type { BottomSheetLegendListProps } from "./bottom-sheet-legend-list";
35
46
  export type { BottomSheetOverlayProps } from "./bottom-sheet-overlay";
36
- export type { BottomSheetPortalProps } from "./bottom-sheet-portal";
47
+ export { BottomSheetProvider, type BottomSheetProviderProps } from "./bottom-sheet-provider";
37
48
  export type { BottomSheetScrollViewProps } from "./bottom-sheet-scroll-view";
49
+ export type { BottomSheetSectionListProps } from "./bottom-sheet-section-list";
50
+ export type { BottomSheetStepProps, BottomSheetStepsProps } from "./bottom-sheet-steps";
51
+ export type { BottomSheetTextInputProps } from "./bottom-sheet-text-input";
38
52
  export type { BottomSheetTitleProps } from "./bottom-sheet-title";
39
53
  export type { BottomSheetTriggerProps } from "./bottom-sheet-trigger";
40
54
  export { type BottomSheetInputHandlers, useBottomSheetInput } from "./use-bottom-sheet-input";
55
+
56
+ /**
57
+ * The imperative handle a `BottomSheet` ref exposes.
58
+ *
59
+ * @deprecated The engine's name is `BottomSheetRef`; this alias stays for the
60
+ * callers that typed the old name and goes with the next major.
61
+ */
62
+ export type BottomSheetHandle = import("@delacour/react-native-bottom-sheet").BottomSheetRef;
@@ -1,125 +1,26 @@
1
- import { useBottomSheetInternal } from "@gorhom/bottom-sheet";
2
- import { type RefCallback, useCallback, useRef } from "react";
3
- import { type BlurEvent, type FocusEvent, findNodeHandle, TextInput } from "react-native";
1
+ import { type BottomSheetTextInputHandlers, useBottomSheetTextInput } from "@delacour/react-native-bottom-sheet";
4
2
 
5
- /**
6
- * `findNodeHandle` for a value React Native's own types do not admit.
7
- *
8
- * `TextInput.State.currentlyFocusedInput()` returns a Fabric host instance, and
9
- * the shipped signature still describes the pre-Fabric parameter — the runtime
10
- * accepts it, which is what gorhom's own `BottomSheetTextInput` relies on. One
11
- * cast, in one place, rather than at each call.
12
- */
13
- function nodeHandleOf(instance: unknown): number | null {
14
- return findNodeHandle(instance as Parameters<typeof findNodeHandle>[0]);
15
- }
16
-
17
- export type BottomSheetInputHandlers = {
18
- onFocus: (event: FocusEvent) => void;
19
- onBlur: (event: BlurEvent) => void;
20
- ref: RefCallback<TextInput | null>;
21
- };
3
+ export type BottomSheetInputHandlers = BottomSheetTextInputHandlers;
22
4
 
23
5
  /**
24
- * Makes this package's `Input` behave like a text field the sheet owns.
6
+ * Registers a text field as the sheet's own, so the keyboard it raises is the
7
+ * sheet's to lift for.
25
8
  *
26
- * Spread the result onto any `TextInput` inside a `BottomSheet`:
9
+ * The engine's `useBottomSheetTextInput()` under the name this library shipped
10
+ * it as. Spread the result onto any `Input` or `TextInput`:
27
11
  *
28
12
  * ```tsx
29
- * <Input {...useBottomSheetInput()} placeholder="Name" />
13
+ * const sheetInput = useBottomSheetInput();
14
+ * <Input {...sheetInput} placeholder="Name" />
30
15
  * ```
31
16
  *
32
- * A sheet only knows to grow for the keyboard when it knows the focused field is
33
- * one of its own. gorhom ships `BottomSheetTextInput` to register that, but
34
- * reaching for it here would mean this library's `Input` — its variants, its
35
- * `Input.Group`, its invalid and disabled cascade — could not be used inside a
36
- * sheet at all. So the registration is exposed as handlers instead, and `Input`
37
- * keeps knowing nothing about `@gorhom/bottom-sheet`.
38
- *
39
- * Three props, and all three are load-bearing:
17
+ * All three handlers matter: `ref` puts the field's native node in the sheet's
18
+ * registry, `onFocus` claims the keyboard, and `onBlur` gives it up — unless
19
+ * focus moved to another registered field in the same sheet, which is what
20
+ * keeps a tap from one field to the next from reading as a close and a reopen.
21
+ * Outside a sheet the handlers are inert, so a form shared between a screen
22
+ * and a sheet needs no branch.
40
23
  *
41
- * - `ref` registers the field's native node with the sheet. Without it, moving
42
- * focus from one field in the sheet to the next reads as the keyboard closing
43
- * and reopening, and the sheet resizes twice on the way.
44
- * - `onFocus` tells the sheet which field to keep clear of the keyboard.
45
- * - `onBlur` gives that up again, unless focus went to another field in the same
46
- * sheet.
47
- *
48
- * Called outside a sheet it returns inert handlers rather than throwing, so a
49
- * form component shared between a screen and a sheet needs no branch.
50
- *
51
- * It covers the keyboard half only. A *drag* across a field can still be claimed
52
- * by the sheet's content pan, because gorhom's input is built on Gesture
53
- * Handler's `TextInput` and this one is not; if that ever bites, the fix is
54
- * `enableContentPanningGesture={false}` on the `BottomSheet.Container`. The
55
- * *tap* half — a scrim that used to swallow the press meant for the field — is
56
- * solved in `BottomSheet.Overlay`, not here.
57
- *
58
- * @example
59
- * <Field>
60
- * <Field.Label>Name</Field.Label>
61
- * <Input {...useBottomSheetInput()} onChangeText={setName} value={name} />
62
- * </Field>
24
+ * `BottomSheet.TextInput` is this over `Input`, for the plain case.
63
25
  */
64
- export function useBottomSheetInput(): BottomSheetInputHandlers {
65
- const internal = useBottomSheetInternal(true);
66
- // The node this field last registered, so the ref can withdraw exactly it.
67
- // A JS ref rather than a shared value: nothing renders differently for it.
68
- const registeredNodeRef = useRef<number | null>(null);
69
-
70
- const ref = useCallback<RefCallback<TextInput | null>>(
71
- (instance) => {
72
- const nodes = internal?.textInputNodesRef.current;
73
- const previous = registeredNodeRef.current;
74
-
75
- if (previous !== null) {
76
- nodes?.delete(previous);
77
- registeredNodeRef.current = null;
78
- if (internal !== null && internal.animatedKeyboardState.get().target === previous) {
79
- internal.animatedKeyboardState.set((state) => ({ ...state, target: undefined }));
80
- }
81
- }
82
-
83
- if (instance === null || nodes === undefined) return;
84
-
85
- const node = nodeHandleOf(instance);
86
- if (node === null) return;
87
-
88
- nodes.add(node);
89
- registeredNodeRef.current = node;
90
- },
91
- [internal]
92
- );
93
-
94
- const onFocus = useCallback(
95
- (event: FocusEvent) => {
96
- if (internal === null) return;
97
- const target = event.nativeEvent.target;
98
- internal.animatedKeyboardState.set((state) => ({ ...state, target }));
99
- },
100
- [internal]
101
- );
102
-
103
- const onBlur = useCallback(
104
- (event: BlurEvent) => {
105
- if (internal === null) return;
106
-
107
- const target = event.nativeEvent.target;
108
- const isThisFieldsTarget = internal.animatedKeyboardState.get().target === target;
109
- if (!isThisFieldsTarget) return;
110
-
111
- // React Native's own focus registry, not the sheet's: focus may already
112
- // have moved to the next field in the same sheet, and giving the target
113
- // up there would close and reopen the sheet's keyboard allowance between
114
- // two taps that never dismissed the keyboard.
115
- const focused = TextInput.State.currentlyFocusedInput();
116
- const focusedNode = focused === null ? null : nodeHandleOf(focused);
117
- if (focusedNode !== null && internal.textInputNodesRef.current.has(focusedNode)) return;
118
-
119
- internal.animatedKeyboardState.set((state) => ({ ...state, target: undefined }));
120
- },
121
- [internal]
122
- );
123
-
124
- return { onBlur, onFocus, ref };
125
- }
26
+ export const useBottomSheetInput: () => BottomSheetInputHandlers = useBottomSheetTextInput;
@@ -194,20 +194,54 @@ for a chunk that says something rather than doing something. An
194
194
  - **The group paints nothing.** No background, no border, no disabled fade. A
195
195
  disabled group publishes `isDisabled` and each member fades itself; a group
196
196
  fading as well would compound the two down to a quarter opacity. It carries no
197
- `gap` either — a gap is the seam this component exists to close — and no
198
- `overflow-hidden`, which would square off the very corners the position
197
+ `gap` while attached — a gap is the seam this component exists to close — and
198
+ no `overflow-hidden`, which would square off the very corners the position
199
199
  compounds just rounded.
200
200
  - **No `accessibilityRole` on the group.** Announcing a control with no action
201
201
  in front of every member helps nobody; the members are already buttons.
202
- - **Horizontal groups are content-width** (`self-start`), because that is what a
203
- segmented control is. For a run that fills its parent, put `className="w-full"`
204
- on the group and `className="flex-1"` on each member — **and make sure the
205
- parent has a definite width**. Yoga resolves a percentage against the parent's
206
- content box, so `w-full` inside a container that is itself content-sized
207
- resolves to nothing and falls back to the content width: the group collapses
208
- onto its buttons and a `flex-1` field between them is squeezed to a few points.
209
- It looks like the group ignored `w-full`. It did not; there was no width to
210
- take a percentage of.
202
+ - **`isAttached={false}` shares the props and drops the shape.** A toolbar of
203
+ `Find · Export · Share` wants one `variant` and one `size` without becoming a
204
+ segmented control. Detached, `resolveGroupPositions` places every member
205
+ `only` — the lone member's existing position, which draws the lone button's
206
+ `rounded-button-*` — rather than minting a fourth sentinel, and
207
+ `resolveGroupSeams` returns no seams, so nothing overlaps into the gap. The gap
208
+ is `gap-2` on either axis, a `tv` variant on the `group` slot and never inline.
209
+ `Button.Group.Separator` still renders, as a rule between spaced members.
210
+ - **A detached member presses with `scale` again.** The fade exists only because
211
+ a scaling member tears its seam, and a detached run has none.
212
+ `resolveButtonFeedback` takes `isAttached` as a fourth argument, defaulting to
213
+ `true`, rather than turning into an options object: its positional signature
214
+ is public, `Input` and the existing tests call it, and the new rung is the only
215
+ thing that changed.
216
+ - **`isFullWidth` splits the parent equally, not by content.** A
217
+ `Monthly · Annual · One-off` picker sized to its labels moves its dividers when
218
+ the copy changes, and a wider segment reads as more important. The group trades
219
+ `self-start` for `w-full self-stretch` — declared after `orientation` in
220
+ `buttonVariants`, so tailwind-merge drops `self-start` rather than keeping it —
221
+ and every member of a horizontal run gets `flex-1 basis-0`. **`basis-0` is the
222
+ part that matters**: with `flex-1` alone Yoga shares out only the space left
223
+ after each member's content width, and the widths still follow the labels.
224
+ - **A member learns it stretches from context, as `isStretched`.**
225
+ `resolveGroupMemberStretch` decides it once per group, it rides on the
226
+ per-member context beside `position` and `isSeamed`, and `Button` feeds it to
227
+ an `isStretched` variant on its own `tv`. No `cloneElement`, for the reason
228
+ above. A caller's `className="flex-none"` on one member still wins — the
229
+ variant is merged before the caller's class. `Button.Group.Text` and the
230
+ separator do **not** stretch: a caption and a rule keep their content width
231
+ and the buttons share what is left. `Input` does not read it yet; a caller
232
+ sizes a joined field with its own `className`.
233
+ - **`isFullWidth` on a vertical group widens the group and does nothing else.**
234
+ Its members already stretch on the cross axis, and `basis-0` on the main axis
235
+ of a column with no definite height would collapse every member to nothing —
236
+ so `isStretched` is `false` there by construction.
237
+ - **Horizontal groups are content-width by default** (`self-start`), because
238
+ that is what a segmented control is. `isFullWidth` fills the parent — **and the
239
+ parent must have a definite width**. Yoga resolves a percentage against the
240
+ parent's content box, so `w-full` inside a container that is itself
241
+ content-sized resolves to nothing and falls back to the content width: the
242
+ group collapses onto its buttons and a `flex-1` field between them is squeezed
243
+ to a few points. It looks like the group ignored `isFullWidth`. It did not;
244
+ there was no width to take a percentage of.
211
245
  - **`Button.Group.Text` draws the button's own chrome**, not chrome of its own,
212
246
  which is what keeps its height, padding and corner identical to the buttons
213
247
  beside it — those come off one axis, and restating any of them here is how a
@@ -215,3 +249,9 @@ for a chunk that says something rather than doing something. An
215
249
  than `primary` when nothing names a variant: a chunk that cannot be pressed
216
250
  should not wear the group's action paint. A group that *does* name a variant
217
251
  is followed, so an outline run reads as one piece.
252
+
253
+ - **`material="etched"` is opt-in and per fill.** `primary` takes `shadow-etched-primary` (a white
254
+ inset top edge at 16%); `secondary`, `tertiary` and `outline` take the light/dark edge
255
+ (`shadow-etched dark:shadow-etched-dark`); `ghost`, `destructive` and `destructive-soft` stay flat
256
+ — a ghost has no surface to lift and a signal colour does not take a second highlight. The press
257
+ scale is already 0.97. The tokens live in `tokens.css`.
@@ -13,6 +13,7 @@ import {
13
13
  type ButtonSize,
14
14
  type ButtonVariant,
15
15
  buttonVariants,
16
+ resolveGroupMemberStretch,
16
17
  resolveGroupPositions,
17
18
  resolveGroupSeams,
18
19
  } from "./button.variants";
@@ -30,6 +31,18 @@ export type ButtonGroupProps = ViewProps & {
30
31
  isDisabled?: boolean;
31
32
  /** Press treatment for every member. Unset, a joined member fades rather than scaling. */
32
33
  feedback?: PressableFeedback;
34
+ /**
35
+ * Join the members into one shape. On by default, because that is what a group is. Off keeps the
36
+ * inherited `variant` / `size` / `isDisabled` / `feedback` and drops the joined shape: every
37
+ * member keeps its own corner, no seam overlap, and the run takes a `gap`.
38
+ */
39
+ isAttached?: boolean;
40
+ /**
41
+ * Span the parent and split it equally between members. Horizontal only — a vertical group
42
+ * already stretches its members, so there it only widens the group. The parent needs a
43
+ * definite width.
44
+ */
45
+ isFullWidth?: boolean;
33
46
  className?: string;
34
47
  children?: ReactNode;
35
48
  };
@@ -40,13 +53,15 @@ function ButtonGroupRoot({
40
53
  variant,
41
54
  isDisabled,
42
55
  feedback,
56
+ isAttached = true,
57
+ isFullWidth = false,
43
58
  className,
44
59
  children,
45
60
  ...props
46
61
  }: ButtonGroupProps): ReactElement {
47
62
  const group = useMemo<ButtonGroupContextValue>(
48
- () => ({ feedback, isDisabled, orientation, size, variant }),
49
- [feedback, isDisabled, orientation, size, variant]
63
+ () => ({ feedback, isAttached, isDisabled, isFullWidth, orientation, size, variant }),
64
+ [feedback, isAttached, isDisabled, isFullWidth, orientation, size, variant]
50
65
  );
51
66
 
52
67
  const items = useMemo(() => Children.toArray(children), [children]);
@@ -56,17 +71,18 @@ function ButtonGroupRoot({
56
71
  // the members' order actually change.
57
72
  const values = useMemo(() => {
58
73
  const isMember = items.map(isGroupMember);
59
- const positions = resolveGroupPositions(isMember);
60
- const seams = resolveGroupSeams(isMember);
74
+ const positions = resolveGroupPositions(isMember, group);
75
+ const seams = resolveGroupSeams(isMember, group);
76
+ const isStretched = resolveGroupMemberStretch(group);
61
77
 
62
78
  return positions.map<ButtonGroupItemContextValue | null>((position, index) =>
63
- position === null ? null : { ...group, isSeamed: seams[index] === true, position }
79
+ position === null ? null : { ...group, isSeamed: seams[index] === true, isStretched, position }
64
80
  );
65
81
  }, [items, group]);
66
82
 
67
83
  return (
68
84
  <ButtonGroupProvider value={group}>
69
- <View className={buttonVariants({ orientation }).group({ className })} {...props}>
85
+ <View className={buttonVariants({ isAttached, isFullWidth, orientation }).group({ className })} {...props}>
70
86
  {items.map((child, index) => {
71
87
  const value = values[index];
72
88
  if (!value) return child;
@@ -130,8 +146,14 @@ function keyOf(child: ReactNode, index: number): string {
130
146
  * The group paints nothing itself — no background, no border, no disabled fade.
131
147
  * A fade here would compound with the members' own down to a quarter opacity.
132
148
  *
133
- * Horizontal groups are content-width. For a run that fills its parent, put
134
- * `className="w-full"` on the group and `className="flex-1"` on each member.
149
+ * Horizontal groups are content-width. `isFullWidth` spans the parent and
150
+ * splits it equally between members — `flex-1 basis-0` on each, published as
151
+ * `isStretched` on the member's context — whatever their labels say. The parent
152
+ * needs a definite width for there to be anything to split.
153
+ *
154
+ * `isAttached={false}` keeps the shared axes and drops the shape: every member
155
+ * draws its own corner, nothing overlaps, the run takes a gap, and an unset
156
+ * `feedback` goes back to the lone button's `scale`.
135
157
  *
136
158
  * @example
137
159
  * <Button.Group variant="outline">
@@ -141,6 +163,20 @@ function keyOf(child: ReactNode, index: number): string {
141
163
  * </Button.Group>
142
164
  *
143
165
  * @example
166
+ * <Button.Group isAttached={false} size="sm" variant="outline">
167
+ * <Button onPress={find}>Find</Button>
168
+ * <Button onPress={exportFile}>Export</Button>
169
+ * <Button onPress={share}>Share</Button>
170
+ * </Button.Group>
171
+ *
172
+ * @example
173
+ * <Button.Group isFullWidth variant="outline">
174
+ * <Button onPress={monthly}>Monthly</Button>
175
+ * <Button onPress={annual}>Annual</Button>
176
+ * <Button onPress={once}>One-off</Button>
177
+ * </Button.Group>
178
+ *
179
+ * @example
144
180
  * <Button.Group>
145
181
  * <Button onPress={save}>Save</Button>
146
182
  * <Button.Group.Separator />