@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
@@ -1,35 +1,44 @@
1
- import type { BottomSheetBackgroundProps as GorhomBottomSheetBackgroundProps } from "@gorhom/bottom-sheet";
1
+ import {
2
+ BottomSheet as Headless,
3
+ type BottomSheetBackgroundProps as HeadlessProps,
4
+ useBottomSheetInternal,
5
+ } from "@delacour/react-native-bottom-sheet";
2
6
  import type { ReactElement } from "react";
3
- import { View } from "react-native";
7
+ import { withUniwind } from "uniwind";
4
8
  import { bottomSheetVariants } from "./bottom-sheet.variants";
5
9
 
6
- export type BottomSheetBackgroundProps = Partial<GorhomBottomSheetBackgroundProps> & {
10
+ type StyledBackgroundComponent = (props: HeadlessProps & { className?: string }) => ReactElement | null;
11
+
12
+ // Third-party to Uniwind, so `className` needs the wrapper — and it has to be
13
+ // built at module scope or every render mints a new component type.
14
+ const StyledBackground = withUniwind(Headless.Background) as unknown as StyledBackgroundComponent;
15
+
16
+ export type BottomSheetBackgroundProps = HeadlessProps & {
7
17
  className?: string;
8
18
  };
9
19
 
10
20
  /**
11
- * The sheet's own surface, drawn behind the handle and the content.
21
+ * The sheet's surface: the box behind the handle and the content.
12
22
  *
13
- * Supplied to gorhom as `backgroundComponent` rather than styled through
14
- * `backgroundStyle`, so the surface is a className like every other surface in
15
- * this package — and so `backgroundClassName` on `BottomSheet.Container` has
16
- * something to reach. A `backgroundStyle` a caller passes still arrives, merged
17
- * into the `style` below.
23
+ * `bg-popover`, top corners rounded — and every corner when the root is
24
+ * `detached`, because a floating card has a bottom edge on screen. The engine
25
+ * sizes it: an attached surface fills the panel and its bottom edge is always
26
+ * off-screen; a detached one is exactly the sheet's height, so the corners this
27
+ * rounds are the ones you see.
18
28
  *
19
- * **Forwarding `style` is load-bearing.** gorhom hands this component
20
- * `[StyleSheet.absoluteFill, backgroundStyle]`; drop it and the surface collapses
21
- * to nothing and the sheet renders transparent over the app.
29
+ * `Container` writes one for you unless you write it — see there.
22
30
  *
23
- * The two animated variables are destructured off rather than spread: they are
24
- * shared values, and a `View` has no use for either.
31
+ * @example
32
+ * <BottomSheet.Background className="bg-card" />
25
33
  */
26
- export function BottomSheetBackground({
27
- animatedIndex: _animatedIndex,
28
- animatedPosition: _animatedPosition,
29
- className,
30
- style,
31
- ...props
32
- }: BottomSheetBackgroundProps): ReactElement {
33
- return <View className={bottomSheetVariants().background({ className })} style={style} {...props} />;
34
+ export function BottomSheetBackground({ className, ...props }: BottomSheetBackgroundProps): ReactElement {
35
+ const { detached } = useBottomSheetInternal();
36
+
37
+ return (
38
+ <StyledBackground
39
+ className={bottomSheetVariants({ detached: detached !== null }).background({ className })}
40
+ {...props}
41
+ />
42
+ );
34
43
  }
35
44
  BottomSheetBackground.displayName = "DelacourUI.BottomSheet.Background";
@@ -1,8 +1,8 @@
1
- import { type ReactElement, useCallback } from "react";
1
+ import { BottomSheet as Headless } from "@delacour/react-native-bottom-sheet";
2
+ import type { ReactElement } from "react";
2
3
  import { IconCrossSmall } from "../../icons/central";
3
4
  import { Icon } from "../icon";
4
5
  import { Pressable, type PressableProps } from "../pressable";
5
- import { useBottomSheetPart } from "./bottom-sheet.context";
6
6
  import { BOTTOM_SHEET_CLOSE_HIT_SLOP, bottomSheetVariants } from "./bottom-sheet.variants";
7
7
 
8
8
  export type BottomSheetCloseProps = Omit<PressableProps, "asChild" | "busy" | "children" | "disabled">;
@@ -10,17 +10,17 @@ export type BottomSheetCloseProps = Omit<PressableProps, "asChild" | "busy" | "c
10
10
  /**
11
11
  * The sheet's dismiss control.
12
12
  *
13
- * Positioned out of the content's flow, in the top-right of the sheet, so it does
14
- * not push the title down or take a row of its own. `BottomSheet.Title` reserves
15
- * the clearance for it on every sheet — see `bottomSheetVariants`.
13
+ * The engine's `Close asChild` around this library's `Pressable`, so the press
14
+ * is the library's — `fade` rather than a spring, because a scale on a glyph
15
+ * this small reads as a jitter, the reason `Badge.CloseButton` presses the same
16
+ * way — and the close is the engine's: the same path a swipe-down and a scrim
17
+ * press take, so a caller has one `onOpenChange` to watch rather than three.
16
18
  *
17
- * Closing goes through the same `onOpenChange` a swipe-down and a backdrop press
18
- * do, so a caller has one callback to watch rather than three.
19
- *
20
- * `fade` rather than a spring: a scale on a glyph this small reads as a jitter,
21
- * the reason `Badge.CloseButton` presses the same way. The slop is there because
22
- * a bare glyph in a corner has no padded capsule to bring it up to the 44pt
23
- * minimum — the case `Checkbox` mints slop for.
19
+ * Positioned out of the content's flow, in the top-right of the sheet, so it
20
+ * does not push the title down or take a row of its own. `BottomSheet.Title`
21
+ * reserves the clearance for it on every sheet — see `bottomSheetVariants`.
22
+ * The slop is there because a bare glyph in a corner has no padded capsule to
23
+ * bring it up to the 44pt minimum — the case `Checkbox` mints slop for.
24
24
  *
25
25
  * @example
26
26
  * <BottomSheet.Content>
@@ -36,25 +36,18 @@ export function BottomSheetClose({
36
36
  onPress,
37
37
  ...props
38
38
  }: BottomSheetCloseProps): ReactElement {
39
- const { close } = useBottomSheetPart("BottomSheet.Close");
40
-
41
- const handlePress = useCallback(() => {
42
- close();
43
- onPress?.();
44
- }, [close, onPress]);
45
-
46
39
  return (
47
- <Pressable
48
- accessibilityLabel={accessibilityLabel}
49
- accessibilityRole="button"
50
- className={bottomSheetVariants().close({ className })}
51
- feedback={feedback}
52
- hitSlop={hitSlop}
53
- onPress={handlePress}
54
- {...props}
55
- >
56
- <Icon color="muted-foreground" icon={IconCrossSmall} />
57
- </Pressable>
40
+ <Headless.Close accessibilityLabel={accessibilityLabel} asChild onPress={onPress}>
41
+ <Pressable
42
+ accessibilityRole="button"
43
+ className={bottomSheetVariants().close({ className })}
44
+ feedback={feedback}
45
+ hitSlop={hitSlop}
46
+ {...props}
47
+ >
48
+ <Icon color="muted-foreground" icon={IconCrossSmall} />
49
+ </Pressable>
50
+ </Headless.Close>
58
51
  );
59
52
  }
60
53
  BottomSheetClose.displayName = "DelacourUI.BottomSheet.Close";
@@ -1,45 +1,27 @@
1
1
  import {
2
- type BottomSheetBackdropProps,
3
- BottomSheetModal,
4
- type BottomSheetModalProps,
5
- type BottomSheetBackgroundProps as GorhomBottomSheetBackgroundProps,
6
- type BottomSheetFooterProps as GorhomBottomSheetFooterProps,
7
- type BottomSheetHandleProps as GorhomBottomSheetHandleProps,
8
- } from "@gorhom/bottom-sheet";
9
- import {
10
- Children,
11
- cloneElement,
12
- isValidElement,
13
- type ReactElement,
14
- type ReactNode,
15
- useCallback,
16
- useEffect,
17
- useMemo,
18
- useRef,
19
- } from "react";
20
- import { useSharedValue } from "react-native-reanimated";
21
- import { useKeyboardAnimationGuard } from "../../hooks/use-keyboard-state-sync";
22
- import {
23
- BottomSheetContainerProvider,
24
- BottomSheetProvider,
25
- useBottomSheetPart,
26
- useBottomSheetPortalContext,
27
- } from "./bottom-sheet.context";
28
- import {
29
- BOTTOM_SHEET_KEYBOARD_DEFAULTS,
30
- type BottomSheetFooterFlag,
31
- type BottomSheetFooterPlacement,
32
- resolveFooterPlacement,
33
- } from "./bottom-sheet.variants";
2
+ BottomSheet as Headless,
3
+ type BottomSheetContainerProps as HeadlessProps,
4
+ } from "@delacour/react-native-bottom-sheet";
5
+ import { Children, isValidElement, type ReactElement, type ReactNode } from "react";
6
+ import { withUniwind } from "uniwind";
7
+ import { cn } from "../../lib/cn";
34
8
  import { BottomSheetBackground } from "./bottom-sheet-background";
35
- import { BottomSheetFooter, type BottomSheetFooterProps } from "./bottom-sheet-footer";
36
9
  import { BottomSheetHandle } from "./bottom-sheet-handle";
37
10
 
38
- export type BottomSheetContainerProps = Omit<
39
- BottomSheetModalProps,
40
- "backdropComponent" | "backgroundComponent" | "children" | "footerComponent" | "handleComponent" | "onDismiss" | "ref"
41
- > & {
42
- children?: ReactNode;
11
+ /**
12
+ * The engine's panel, taught `className`.
13
+ *
14
+ * `withUniwind`'s return type maps over a component's props and loses the
15
+ * shape of anything animated, so the signature is restated. Built at module
16
+ * scope: in render it would mint a new component type every pass and remount
17
+ * the whole sheet.
18
+ */
19
+ type StyledContainerComponent = (props: HeadlessProps & { className?: string }) => ReactElement | null;
20
+
21
+ const StyledContainer = withUniwind(Headless.Container) as unknown as StyledContainerComponent;
22
+
23
+ export type BottomSheetContainerProps = HeadlessProps & {
24
+ className?: string;
43
25
  /** Classes for the sheet's surface — the box behind the handle and the content. */
44
26
  backgroundClassName?: string;
45
27
  /** Classes for the row the grabber sits in. */
@@ -48,43 +30,29 @@ export type BottomSheetContainerProps = Omit<
48
30
  handleIndicatorClassName?: string;
49
31
  };
50
32
 
33
+ /** Whether any direct child is one of the two parts the container would otherwise write itself. */
34
+ function hasSurfaceParts(children: ReactNode): boolean {
35
+ return Children.toArray(children).some(
36
+ (child) => isValidElement(child) && (child.type === BottomSheetBackground || child.type === BottomSheetHandle)
37
+ );
38
+ }
39
+
51
40
  /**
52
- * The sheet itself: the surface that slides up, and everything inside it.
53
- *
54
- * Every gorhom prop passes through, so snap points, dynamic sizing, the pan
55
- * gestures and the animation configs are all reachable. Four are not, because
56
- * this component supplies them: the backdrop and the footer are cloned from the
57
- * parts a caller wrote, and the background and the handle are rendered as views
58
- * of this package's own so they can take a `className` rather than a style.
59
- *
60
- * **It takes no `className`.** gorhom's modal wears an animated style that the
61
- * sheet's own position writes to every frame, so there is nothing here for a
62
- * class to reach that `backgroundClassName` does not already reach better. The
63
- * same trade `Screen.Footer` makes — that file takes a `style` and never a
64
- * `className`, and so wraps nothing in `withUniwind`.
41
+ * The panel — the surface that moves.
65
42
  *
66
- * **Presenting is driven by state, never by the ref.** `isOpen` on the root is
67
- * the one source, so a swipe-down, a backdrop press, `BottomSheet.Close` and a
68
- * programmatic close all arrive at the same `onOpenChange`. gorhom's own
69
- * `onDismiss` is therefore not forwarded: two callbacks for one event is two
70
- * things that can disagree. `useBottomSheet().sheetRef` is still there for the
71
- * imperative calls with no declarative form — `snapToIndex`, `expand`.
43
+ * The engine positions it, sizes it to the frame and translates it by the
44
+ * sheet's geometry; this adds classes and the two parts every sheet has.
72
45
  *
73
- * **`accessible` defaults to `false`, and that is not a small thing.** gorhom
74
- * marks the sheet's content container `accessible` with the label "Bottom
75
- * Sheet" and the `adjustable` role. On iOS an `accessible` container collapses
76
- * its whole subtree into ONE element, so every field, button and line of copy in
77
- * the sheet becomes unreachable to VoiceOver — a form in a sheet cannot be
78
- * filled in at all. This is `Field`'s rule one component along: the row is
79
- * `accessible={false}` so the control stays the element a screen reader sees.
80
- * Pass `accessible` to opt back into gorhom's behaviour for a sheet whose body
81
- * really is one adjustable thing.
46
+ * **It writes `Background` and `Handle` for you** when the children hold
47
+ * neither, so the common sheet is `Container > Content` and nothing else. Write
48
+ * either one yourself and it writes nothing — a container with a custom handle
49
+ * and no surface is a decision, not an omission. The three `*ClassName` props
50
+ * reach the parts it writes; a part you write takes its own `className`.
82
51
  *
83
- * Three defaults differ from gorhom's. `enablePanDownToClose` is on, because a
84
- * modal sheet has no collapsed resting state to pan down to. And the keyboard
85
- * trio comes from `BOTTOM_SHEET_KEYBOARD_DEFAULTS` — see that const for why
86
- * Android must not be left on `adjustPan`. Both are ordinary props, so either
87
- * can be turned back off.
52
+ * **Sizing and behaviour live on the root, not here.** `snapPoints`,
53
+ * `dynamicSizing`, `maxDynamicContentSize`, `keyboardBehavior` and the rest
54
+ * are `BottomSheet`'s props: the engine resolves them once for every part, and
55
+ * a panel that carried them would be a second place to look.
88
56
  *
89
57
  * @example
90
58
  * <BottomSheet.Container>
@@ -92,155 +60,33 @@ export type BottomSheetContainerProps = Omit<
92
60
  * </BottomSheet.Container>
93
61
  *
94
62
  * @example
95
- * // A scrollable sheet needs a fixed height to scroll within.
96
- * <BottomSheet.Container enableDynamicSizing={false} snapPoints={["50%", "90%"]}>
97
- * <BottomSheet.ScrollView>{rows}</BottomSheet.ScrollView>
98
- * <BottomSheet.Footer sticky>{actions}</BottomSheet.Footer>
63
+ * // A custom grabber; the surface still comes with it.
64
+ * <BottomSheet.Container backgroundClassName="bg-card">
65
+ * <BottomSheet.Background className="bg-card" />
66
+ * <BottomSheet.Handle><MyGrabber /></BottomSheet.Handle>
67
+ * <BottomSheet.Content>{…}</BottomSheet.Content>
99
68
  * </BottomSheet.Container>
100
69
  */
101
70
  export function BottomSheetContainer({
102
- accessible = false,
103
71
  backgroundClassName,
104
72
  children,
105
- enablePanDownToClose = true,
73
+ className,
106
74
  handleClassName,
107
75
  handleIndicatorClassName,
108
76
  ...props
109
77
  }: BottomSheetContainerProps): ReactElement {
110
- const sheet = useBottomSheetPart("BottomSheet.Container");
111
- const { isOpen, setOpen, sheetRef } = sheet;
112
- const portal = useBottomSheetPortalContext();
113
- // A sheet that opens while `KeyboardProvider`'s shared values are pinned open
114
- // by a keyboard that vanished without a `will` event would size itself for a
115
- // keyboard that is not there. `Screen.Footer` runs the same repair on mount.
116
- useKeyboardAnimationGuard();
117
-
118
- // Whether this sheet has ever been presented. A JS ref rather than state:
119
- // nothing renders differently for it, and it has to be readable inside the
120
- // effect that sets it.
121
- const hasPresentedRef = useRef(false);
122
-
123
- const { content, footer, placement } = useMemo(() => hoistFooter(children), [children]);
124
- const hasStickyFooter = placement === "sticky";
125
- const overlay = portal?.overlay ?? null;
126
-
127
- // Written by the pinned footer's own onLayout, read by the body — the
128
- // content's spacer, or the scroll view's margin. It lives here because the two
129
- // are siblings in gorhom's tree and neither can see the other — the footer is
130
- // rendered outside the children this file wraps.
131
- const footerHeight = useSharedValue(0);
132
-
133
- const containerValue = useMemo(() => ({ footerHeight, hasStickyFooter }), [footerHeight, hasStickyFooter]);
134
-
135
- const backdropComponent = useMemo(
136
- () =>
137
- overlay === null ? undefined : (backdropProps: BottomSheetBackdropProps) => cloneElement(overlay, backdropProps),
138
- [overlay]
78
+ const surface = hasSurfaceParts(children) ? null : (
79
+ <>
80
+ <BottomSheetBackground className={backgroundClassName} />
81
+ <BottomSheetHandle className={handleClassName} indicatorClassName={handleIndicatorClassName} />
82
+ </>
139
83
  );
140
84
 
141
- const footerComponent = useMemo(
142
- () =>
143
- footer === null
144
- ? undefined
145
- : (footerProps: GorhomBottomSheetFooterProps) => cloneElement(footer, { ...footerProps, footerHeight }),
146
- [footer, footerHeight]
147
- );
148
-
149
- const backgroundComponent = useCallback(
150
- (backgroundProps: GorhomBottomSheetBackgroundProps) => (
151
- <BottomSheetBackground {...backgroundProps} className={backgroundClassName} />
152
- ),
153
- [backgroundClassName]
154
- );
155
-
156
- const handleComponent = useCallback(
157
- (handleProps: GorhomBottomSheetHandleProps) => (
158
- <BottomSheetHandle {...handleProps} className={handleClassName} indicatorClassName={handleIndicatorClassName} />
159
- ),
160
- [handleClassName, handleIndicatorClassName]
161
- );
162
-
163
- const handleDismiss = useCallback(() => {
164
- hasPresentedRef.current = false;
165
- setOpen(false);
166
- }, [setOpen]);
167
-
168
- useEffect(() => {
169
- if (isOpen) {
170
- hasPresentedRef.current = true;
171
- sheetRef.current?.present();
172
- return;
173
- }
174
-
175
- // Dismissing a modal that was never presented is NOT a no-op, and this is
176
- // the sharpest edge in the component. gorhom's modal starts at status
177
- // `INITIAL`; `dismiss()` reads that as a live sheet being closed and moves
178
- // it to `DISMISSING`, where it stays. The next `present()` then mounts the
179
- // portal and renders the sheet — and it never animates in, never lays out
180
- // its content, and never fires `onChange`. Nothing is logged, nothing
181
- // throws, and the screen simply does not change.
182
- if (!hasPresentedRef.current) return;
183
- hasPresentedRef.current = false;
184
- sheetRef.current?.dismiss();
185
- }, [isOpen, sheetRef]);
186
-
187
85
  return (
188
- <BottomSheetModal
189
- accessible={accessible}
190
- backdropComponent={backdropComponent}
191
- backgroundComponent={backgroundComponent}
192
- enablePanDownToClose={enablePanDownToClose}
193
- footerComponent={footerComponent}
194
- handleComponent={handleComponent}
195
- onDismiss={handleDismiss}
196
- ref={sheetRef}
197
- {...BOTTOM_SHEET_KEYBOARD_DEFAULTS}
198
- {...props}
199
- >
200
- <BottomSheetProvider value={sheet}>
201
- <BottomSheetContainerProvider value={containerValue}>{content}</BottomSheetContainerProvider>
202
- </BottomSheetProvider>
203
- </BottomSheetModal>
86
+ <StyledContainer className={cn(className)} {...props}>
87
+ {surface}
88
+ {children}
89
+ </StyledContainer>
204
90
  );
205
91
  }
206
92
  BottomSheetContainer.displayName = "DelacourUI.BottomSheet.Container";
207
-
208
- /**
209
- * Splits a container's children into the body and a footer to be pinned.
210
- *
211
- * gorhom takes a footer as a `footerComponent` render prop rather than as a
212
- * child, so a sticky one has to leave the tree it was written in; an inline one
213
- * stays put and is simply part of the body. {@link resolveFooterPlacement} is the
214
- * decision and is pure, so `bun test` reaches the whole matrix — this walk only
215
- * turns children into the flags it reads.
216
- *
217
- * Lives here rather than with the parts: it is the container that lifts its own
218
- * children, and importing it from `bottom-sheet-footer.tsx` would close a cycle.
219
- * See AGENTS.md.
220
- */
221
- function hoistFooter(children: ReactNode): {
222
- content: ReactNode;
223
- footer: ReactElement<BottomSheetFooterProps> | null;
224
- placement: BottomSheetFooterPlacement;
225
- } {
226
- const items = Children.toArray(children);
227
- const flags: BottomSheetFooterFlag[] = [];
228
- let footer: ReactElement<BottomSheetFooterProps> | null = null;
229
-
230
- for (const child of items) {
231
- if (!isValidElement(child) || child.type !== BottomSheetFooter) {
232
- flags.push({ isFooter: false, isSticky: false });
233
- continue;
234
- }
235
-
236
- const element = child as ReactElement<BottomSheetFooterProps>;
237
- const isSticky = element.props.sticky === true;
238
- flags.push({ isFooter: true, isSticky });
239
- if (isSticky) footer ??= element;
240
- }
241
-
242
- const placement = resolveFooterPlacement(flags);
243
- if (placement !== "sticky" || footer === null) return { content: items, footer: null, placement };
244
-
245
- return { content: items.filter((child) => child !== footer), footer, placement };
246
- }
@@ -1,12 +1,18 @@
1
- import { BottomSheetView } from "@gorhom/bottom-sheet";
1
+ import {
2
+ BottomSheet as Headless,
3
+ type BottomSheetContentProps as HeadlessProps,
4
+ } from "@delacour/react-native-bottom-sheet";
2
5
  import type { ReactElement } from "react";
3
- import { View, type ViewProps } from "react-native";
4
- import Animated, { useAnimatedStyle } from "react-native-reanimated";
5
- import { useSafeAreaInsets } from "react-native-safe-area-context";
6
- import { useBottomSheetContainerContext } from "./bottom-sheet.context";
7
- import { BOTTOM_SHEET_FOOTER_GAP, bottomSheetVariants, resolveSheetBottomInset } from "./bottom-sheet.variants";
6
+ import { withUniwind } from "uniwind";
7
+ import { BOTTOM_SHEET_FOOTER_GAP, bottomSheetVariants } from "./bottom-sheet.variants";
8
8
 
9
- export type BottomSheetContentProps = ViewProps & {
9
+ type StyledContentComponent = (props: HeadlessProps & { className?: string }) => ReactElement | null;
10
+
11
+ // Built once at module scope; the classes land on the engine's inner, measured
12
+ // view, which is the one that takes `style`.
13
+ const StyledContent = withUniwind(Headless.Content) as unknown as StyledContentComponent;
14
+
15
+ export type BottomSheetContentProps = HeadlessProps & {
10
16
  className?: string;
11
17
  };
12
18
 
@@ -14,28 +20,12 @@ export type BottomSheetContentProps = ViewProps & {
14
20
  * The sheet's body — what a caller writes the title, the copy and the controls
15
21
  * into.
16
22
  *
17
- * Two boxes, and neither is a `withUniwind` wrapper. gorhom's `BottomSheetView`
18
- * is the outer one: it measures itself into the sheet's layout state, which is
19
- * what makes `enableDynamicSizing` size the sheet to whatever is inside. The
20
- * inner `View` is an ordinary React Native one and carries every class, so this
21
- * file wraps nothing — the same move `Screen.Footer` makes with
22
- * `KeyboardStickyView`.
23
- *
24
- * **A pinned footer is reserved by a spacer, not by gorhom's
25
- * `enableFooterMarginAdjustment`.** That prop turns the footer's measured height
26
- * into content-container padding through React state — a commit on every frame
27
- * of the keyboard animation, because the footer changes height as it gives up
28
- * its safe-area band. A spacer whose height is an animated style off the same
29
- * shared value costs nothing on the JS thread, and `Screen` already reserves its
30
- * own chrome exactly this way. The spacer sits OUTSIDE the classed box so the
31
- * content's own `gap` does not land in front of it and quietly double the
32
- * {@link BOTTOM_SHEET_FOOTER_GAP} the footer is held off by.
33
- *
34
- * **The safe-area band goes on the OUTER box, and only when nothing else is
35
- * paying for it** — a pinned footer's own box carries it, so
36
- * {@link resolveSheetBottomInset} withholds it here. The band comes from
37
- * `useSafeAreaInsets()` and never from `pb-safe`, which compiles to
38
- * `env(safe-area-inset-bottom)` and resolves to zero on React Native.
23
+ * The engine's `Content` with the library's gutter and gap on its measured
24
+ * box. Everything the old body had to do by hand is the engine's now: the
25
+ * body is clamped to the space the handle, a sticky footer, the keyboard and
26
+ * the safe-area band leave; a spacer the footer's height plus `footerGap`
27
+ * keeps the last line above a pinned footer; and the band under it is
28
+ * reserved by the geometry rather than padded here.
39
29
  *
40
30
  * @example
41
31
  * <BottomSheet.Content>
@@ -43,24 +33,11 @@ export type BottomSheetContentProps = ViewProps & {
43
33
  * <BottomSheet.Description>This cannot be undone.</BottomSheet.Description>
44
34
  * </BottomSheet.Content>
45
35
  */
46
- export function BottomSheetContent({ className, children, style, ...props }: BottomSheetContentProps): ReactElement {
47
- const container = useBottomSheetContainerContext();
48
- const { bottom } = useSafeAreaInsets();
49
- const hasStickyFooter = container?.hasStickyFooter ?? false;
50
- const footerHeight = container?.footerHeight;
51
- const paddingBottom = resolveSheetBottomInset({ bottom, hasStickyFooter });
52
-
53
- const reserve = useAnimatedStyle(() => ({
54
- height: footerHeight === undefined ? 0 : footerHeight.value + BOTTOM_SHEET_FOOTER_GAP,
55
- }));
56
-
57
- return (
58
- <BottomSheetView style={{ paddingBottom }}>
59
- <View className={bottomSheetVariants().content({ className })} style={style} {...props}>
60
- {children}
61
- </View>
62
- {hasStickyFooter ? <Animated.View style={reserve} /> : null}
63
- </BottomSheetView>
64
- );
36
+ export function BottomSheetContent({
37
+ className,
38
+ footerGap = BOTTOM_SHEET_FOOTER_GAP,
39
+ ...props
40
+ }: BottomSheetContentProps): ReactElement {
41
+ return <StyledContent className={bottomSheetVariants().content({ className })} footerGap={footerGap} {...props} />;
65
42
  }
66
43
  BottomSheetContent.displayName = "DelacourUI.BottomSheet.Content";
@@ -1,3 +1,4 @@
1
+ import { BottomSheet as Headless } from "@delacour/react-native-bottom-sheet";
1
2
  import type { ReactElement } from "react";
2
3
  import { cn } from "../../lib/cn";
3
4
  import { Text, type TextPresetProps } from "../text";
@@ -10,7 +11,8 @@ export type BottomSheetDescriptionProps = TextPresetProps;
10
11
  * *Is* a `Text.Paragraph`, defaulting to the muted token — the title is what the
11
12
  * sheet is about and this is what it means, so the two need to read as a
12
13
  * hierarchy rather than as two equal lines. `color` is an ordinary prop, so a
13
- * description that needs the page colour asks for it.
14
+ * description that needs the page colour asks for it. The engine's
15
+ * `Description asChild` gives it the `nativeID` a skin can describe the panel by.
14
16
  *
15
17
  * It has no slot in `bottomSheetVariants`, and that is deliberate: it carries no
16
18
  * layout of its own, and `tv` emits `undefined` for an empty class string, so a
@@ -24,6 +26,10 @@ export function BottomSheetDescription({
24
26
  color = "muted",
25
27
  ...props
26
28
  }: BottomSheetDescriptionProps): ReactElement {
27
- return <Text.Paragraph className={cn(className)} color={color} {...props} />;
29
+ return (
30
+ <Headless.Description asChild>
31
+ <Text.Paragraph className={cn(className)} color={color} {...props} />
32
+ </Headless.Description>
33
+ );
28
34
  }
29
35
  BottomSheetDescription.displayName = "DelacourUI.BottomSheet.Description";
@@ -0,0 +1,65 @@
1
+ import {
2
+ BottomSheet as Headless,
3
+ type BottomSheetFlatListProps as HeadlessProps,
4
+ useBottomSheetInternal,
5
+ } from "@delacour/react-native-bottom-sheet";
6
+ import type { ReactElement, Ref } from "react";
7
+ import type { FlatList } from "react-native";
8
+ import { withUniwind } from "uniwind";
9
+ import { cn } from "../../lib/cn";
10
+ import { BOTTOM_SHEET_FOOTER_GAP, bottomSheetVariants } from "./bottom-sheet.variants";
11
+
12
+ /**
13
+ * The class props `withUniwind` adds, restated so the generic survives.
14
+ *
15
+ * `withUniwind`'s own return type maps over a component's props and collapses
16
+ * `<ItemT>` in the process, which would leave `data` and `renderItem` checking
17
+ * against `unknown`. Writing the signature out keeps inference and still
18
+ * declares exactly the two props the wrapper adds — the same move
19
+ * `Screen.LegendList` makes.
20
+ */
21
+ type StyledFlatListComponent = <ItemT>(
22
+ props: HeadlessProps<ItemT> & { ref?: Ref<FlatList<ItemT>>; className?: string; contentContainerClassName?: string }
23
+ ) => ReactElement | null;
24
+
25
+ // Built at module scope, or every render mints a new component type and
26
+ // remounts the list.
27
+ const StyledFlatList = withUniwind(Headless.FlatList) as unknown as StyledFlatListComponent;
28
+
29
+ export type BottomSheetFlatListProps<ItemT> = HeadlessProps<ItemT> & {
30
+ ref?: Ref<FlatList<ItemT>>;
31
+ className?: string;
32
+ /** Classes for the content container. Same gutter and gap as `Content`. */
33
+ contentContainerClassName?: string;
34
+ };
35
+
36
+ /**
37
+ * A virtualised body.
38
+ *
39
+ * The engine's `FlatList` — the scroll lock, the drag budget and content size
40
+ * as the dynamic snap point are all its — with the library's gutter on the content
41
+ * container. A virtualised list has no inner box to put the classes on, so
42
+ * here they go on `contentContainerClassName`; the engine flattens the
43
+ * resulting style to one object before the list measures it.
44
+ *
45
+ * @example
46
+ * <BottomSheet.FlatList data={rows} keyExtractor={(row) => row.id} renderItem={renderRow} />
47
+ */
48
+ export function BottomSheetFlatList<ItemT>({
49
+ className,
50
+ contentContainerClassName,
51
+ contentContainerStyle,
52
+ ...props
53
+ }: BottomSheetFlatListProps<ItemT>): ReactElement {
54
+ const { hasFooter } = useBottomSheetInternal();
55
+
56
+ return (
57
+ <StyledFlatList
58
+ className={cn(className)}
59
+ contentContainerClassName={bottomSheetVariants().scrollContent({ className: contentContainerClassName })}
60
+ contentContainerStyle={[{ paddingBottom: hasFooter ? BOTTOM_SHEET_FOOTER_GAP : 0 }, contentContainerStyle]}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+ BottomSheetFlatList.displayName = "DelacourUI.BottomSheet.FlatList";