@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
@@ -0,0 +1,50 @@
1
+ import type { ReactElement } from "react";
2
+ import { I18nManager, View } from "react-native";
3
+ import Animated, { interpolate, useAnimatedStyle } from "react-native-reanimated";
4
+ import { IconChevronRight } from "../../icons/central";
5
+ import { Icon } from "../icon";
6
+ import { useMenuSubPart } from "./menu.context";
7
+ import type { MenuItemProps } from "./menu.types";
8
+ import { MENU_INDICATOR_TOKEN, MENU_SUB_ROTATION, menuVariants, resolveMenuItemAccessibility } from "./menu.variants";
9
+ import { MenuRow } from "./menu-item";
10
+
11
+ export type MenuSubTriggerProps = Omit<MenuItemProps, "onSelect" | "isClosedOnSelect" | "trailing">;
12
+
13
+ /**
14
+ * The row that expands a `Menu.Sub`. Never closes the menu.
15
+ *
16
+ * Its chevron points to the trailing edge and turns 90° to point down as the
17
+ * rows open, off the submenu's own `progress`, so it and the height cannot drift.
18
+ * Under RTL the chevron is mirrored on a wrapper view, so it points left and
19
+ * still turns down. Announced as `expanded`.
20
+ */
21
+ export function MenuSubTrigger({ isDisabled = false, ...props }: MenuSubTriggerProps): ReactElement {
22
+ const { isOpen, progress, toggle } = useMenuSubPart("Menu.SubTrigger");
23
+ const slots = menuVariants();
24
+ const collapsed = MENU_SUB_ROTATION.collapsed;
25
+ const expanded = MENU_SUB_ROTATION.expanded;
26
+
27
+ const rotationStyle = useAnimatedStyle(() => ({
28
+ transform: [{ rotate: `${interpolate(progress.value, [0, 1], [collapsed, expanded])}deg` }],
29
+ }));
30
+
31
+ return (
32
+ <MenuRow
33
+ accessibilityState={resolveMenuItemAccessibility({ isDisabled, isExpanded: isOpen, kind: "sub-trigger" })}
34
+ isDisabled={isDisabled}
35
+ kind="sub-trigger"
36
+ onSelect={toggle}
37
+ trailing={
38
+ <View className={slots.indicator()} style={I18nManager.isRTL ? mirrored : undefined}>
39
+ <Animated.View style={rotationStyle}>
40
+ <Icon className={slots.itemIcon()} color={MENU_INDICATOR_TOKEN} icon={IconChevronRight} />
41
+ </Animated.View>
42
+ </View>
43
+ }
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+ MenuSubTrigger.displayName = "DelacourUI.Menu.SubTrigger";
49
+
50
+ const mirrored = { transform: [{ scaleX: -1 }] };
@@ -0,0 +1,75 @@
1
+ import { type ReactElement, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { View } from "react-native";
3
+ import { cancelAnimation, useSharedValue, withSpring } from "react-native-reanimated";
4
+ import { useControllableState } from "../../hooks/use-controllable-state";
5
+ import { type MenuSubContextValue, MenuSubProvider } from "./menu.context";
6
+ import { MENU_SUB_SPRING } from "./menu.variants";
7
+
8
+ export type MenuSubProps = {
9
+ children: ReactNode;
10
+ isOpen?: boolean;
11
+ defaultOpen?: boolean;
12
+ onOpenChange?: (isOpen: boolean) => void;
13
+ };
14
+
15
+ /**
16
+ * A nested group of rows that expands in place under its `Menu.SubTrigger`.
17
+ *
18
+ * It never flies out to a second panel: on a phone there is no room beside the
19
+ * first, and a second layer over the first hides where the user came from. The
20
+ * rows push the ones below down, with `Collapsible`'s measured height and spring.
21
+ *
22
+ * @example
23
+ * <Menu.Sub>
24
+ * <Menu.SubTrigger icon={IconFolder1}>Move to</Menu.SubTrigger>
25
+ * <Menu.SubContent>
26
+ * <Menu.Item onSelect={moveToInbox}>Inbox</Menu.Item>
27
+ * </Menu.SubContent>
28
+ * </Menu.Sub>
29
+ */
30
+ export function MenuSub({
31
+ children,
32
+ isOpen: isOpenProp,
33
+ defaultOpen = false,
34
+ onOpenChange,
35
+ }: MenuSubProps): ReactElement {
36
+ const changeRef = useRef(onOpenChange);
37
+ changeRef.current = onOpenChange;
38
+ const handleChange = useCallback((next: boolean) => changeRef.current?.(next), []);
39
+
40
+ const [isOpen, setOpen] = useControllableState<boolean>({
41
+ defaultValue: defaultOpen,
42
+ onChange: handleChange,
43
+ value: isOpenProp,
44
+ });
45
+
46
+ const openRef = useRef(isOpen);
47
+ openRef.current = isOpen;
48
+ const toggle = useCallback(() => setOpen(!openRef.current), [setOpen]);
49
+
50
+ const progress = useSharedValue(isOpen ? 1 : 0);
51
+ const contentHeight = useSharedValue(-1);
52
+
53
+ // React state, never a JS-thread read of `contentHeight` — the write is queued
54
+ // onto the UI runtime and a read straight after it can still see -1.
55
+ const [isMeasured, setMeasured] = useState(false);
56
+ const onMeasured = useCallback(() => setMeasured(true), []);
57
+
58
+ useEffect(() => {
59
+ if (isOpen && !isMeasured) return;
60
+ progress.value = withSpring(isOpen ? 1 : 0, MENU_SUB_SPRING);
61
+ return () => cancelAnimation(progress);
62
+ }, [isMeasured, isOpen, progress]);
63
+
64
+ const context = useMemo<MenuSubContextValue>(
65
+ () => ({ contentHeight, isOpen, onMeasured, progress, toggle }),
66
+ [contentHeight, isOpen, onMeasured, progress, toggle]
67
+ );
68
+
69
+ return (
70
+ <MenuSubProvider value={context}>
71
+ <View>{children}</View>
72
+ </MenuSubProvider>
73
+ );
74
+ }
75
+ MenuSub.displayName = "DelacourUI.Menu.Sub";
@@ -0,0 +1,57 @@
1
+ import { type ComponentRef, type ReactElement, useCallback, useMemo } from "react";
2
+ import type Animated from "react-native-reanimated";
3
+ import { composeRefs } from "../../lib/compose-refs";
4
+ import { Slot } from "../../lib/slot";
5
+ import { Pressable, type PressableProps } from "../pressable";
6
+ import { type MenuMeasurable, useMenuPart } from "./menu.context";
7
+ import { MENU_TRIGGER_HINT } from "./menu.variants";
8
+
9
+ export type MenuTriggerProps = PressableProps & { asChild?: boolean };
10
+
11
+ /**
12
+ * The control that opens the menu, and closes it again.
13
+ *
14
+ * A press measures the trigger with `measureInWindow`, anchors the panel to
15
+ * that rect and opens. Announced `expanded` while open, with the hint
16
+ * "Opens a menu".
17
+ *
18
+ * **`asChild` donates the press rather than wrapping the child**, as
19
+ * `BottomSheet.Trigger` does: two nested taps are not simultaneous, and Gesture
20
+ * Handler gives the touch to the descendant — a `Button` wrapped in a pressable
21
+ * trigger would win the tap and the menu would never open. The child receives
22
+ * `onPress` (chained ahead of its own), the ref the trigger measures, and the
23
+ * accessibility props, through `Slot`. So the child has to be something that
24
+ * handles `onPress` — a `Button`, a `ListGroup.Item`, anything on `Pressable`.
25
+ *
26
+ * @example
27
+ * <Menu.Trigger asChild>
28
+ * <Button variant="outline">Options</Button>
29
+ * </Menu.Trigger>
30
+ */
31
+ export function MenuTrigger({ asChild = false, children, onPress, ref, ...props }: MenuTriggerProps): ReactElement {
32
+ const { isOpen, toggle, triggerRef } = useMenuPart("Menu.Trigger");
33
+
34
+ const handlePress = useCallback(() => {
35
+ toggle();
36
+ onPress?.();
37
+ }, [onPress, toggle]);
38
+
39
+ const setTrigger = useCallback(
40
+ (node: ComponentRef<typeof Animated.View> | null) => {
41
+ triggerRef.current = node as MenuMeasurable | null;
42
+ },
43
+ [triggerRef]
44
+ );
45
+ const composedRef = useMemo(() => composeRefs(ref, setTrigger), [ref, setTrigger]);
46
+
47
+ const shared = {
48
+ accessibilityHint: MENU_TRIGGER_HINT,
49
+ accessibilityState: { expanded: isOpen },
50
+ onPress: handlePress,
51
+ ref: composedRef,
52
+ ...props,
53
+ };
54
+
55
+ return asChild ? <Slot {...shared}>{children}</Slot> : <Pressable {...shared}>{children}</Pressable>;
56
+ }
57
+ MenuTrigger.displayName = "DelacourUI.Menu.Trigger";
@@ -0,0 +1,214 @@
1
+ import {
2
+ createContext,
3
+ type ReactElement,
4
+ type ReactNode,
5
+ type RefObject,
6
+ use,
7
+ useCallback,
8
+ useEffect,
9
+ useMemo,
10
+ useRef,
11
+ useState,
12
+ } from "react";
13
+ import type { View } from "react-native";
14
+ import type { SharedValue } from "react-native-reanimated";
15
+ import { useControllableState } from "../../hooks/use-controllable-state";
16
+ import type { HapticFeedback } from "../pressable/pressable";
17
+ import type { MenuAnchorRect } from "./menu.variants";
18
+
19
+ /** Anything `measureInWindow` can be called on — a host view, or an animated one. */
20
+ export type MenuMeasurable = Pick<View, "measureInWindow">;
21
+
22
+ export type MenuContextValue = {
23
+ isOpen: boolean;
24
+ /** Where the panel is anchored, set on open. `null` until the first open. */
25
+ anchor: MenuAnchorRect | null;
26
+ /**
27
+ * Opens the menu. With an `anchor` it opens there — ContextMenu passes a
28
+ * zero-size point. Without one it measures the trigger first.
29
+ */
30
+ open: (anchor?: MenuAnchorRect) => void;
31
+ close: () => void;
32
+ /** Closes an open menu, opens a closed one from the trigger. */
33
+ toggle: () => void;
34
+ /** The trigger's node, measured on open. */
35
+ triggerRef: RefObject<MenuMeasurable | null>;
36
+ /** Played when a row is chosen. */
37
+ haptic: false | HapticFeedback;
38
+ };
39
+
40
+ const MenuContext = createContext<MenuContextValue | null>(null);
41
+
42
+ /**
43
+ * The menu's state provider.
44
+ *
45
+ * Exported so ContextMenu can drive the open state with an anchor of its own:
46
+ * build a value with {@link useMenuRootValue} and wrap `Menu.Content` in this,
47
+ * without importing the `Menu` root at all.
48
+ */
49
+ export function MenuRootProvider({ value, children }: { value: MenuContextValue; children: ReactNode }): ReactElement {
50
+ return <MenuContext value={value}>{children}</MenuContext>;
51
+ }
52
+ MenuRootProvider.displayName = "DelacourUI.Menu.RootProvider";
53
+
54
+ /** The menu's open state and controls, for a custom part. Throws outside a menu. */
55
+ export function useMenu(): Pick<MenuContextValue, "isOpen" | "open" | "close"> {
56
+ const { isOpen, open, close } = useMenuPart("useMenu");
57
+ return { close, isOpen, open };
58
+ }
59
+
60
+ /** The whole context, for the menu's own parts. Throws with the part's name outside a menu. */
61
+ export function useMenuPart(part: string): MenuContextValue {
62
+ const context = use(MenuContext);
63
+ if (!context) throw new Error(`${part} must be used inside a <Menu>.`);
64
+ return context;
65
+ }
66
+
67
+ export type UseMenuRootValueOptions = {
68
+ isOpen?: boolean;
69
+ defaultOpen?: boolean;
70
+ onOpenChange?: (isOpen: boolean) => void;
71
+ haptic?: false | HapticFeedback;
72
+ };
73
+
74
+ /**
75
+ * Builds a menu's context value: the controllable open state, the anchor, and
76
+ * the trigger measurement.
77
+ *
78
+ * The root calls it; ContextMenu calls it too, and opens with `open(point)`.
79
+ * Callbacks are ref-backed, so the value keeps its identity across a caller
80
+ * passing a fresh `onOpenChange` every render.
81
+ */
82
+ export function useMenuRootValue({
83
+ isOpen: isOpenProp,
84
+ defaultOpen = false,
85
+ onOpenChange,
86
+ haptic = false,
87
+ }: UseMenuRootValueOptions): MenuContextValue {
88
+ const changeRef = useRef(onOpenChange);
89
+ changeRef.current = onOpenChange;
90
+ const handleChange = useCallback((next: boolean) => changeRef.current?.(next), []);
91
+
92
+ const [isOpen, setOpen] = useControllableState<boolean>({
93
+ defaultValue: defaultOpen,
94
+ onChange: handleChange,
95
+ value: isOpenProp,
96
+ });
97
+
98
+ const [anchor, setAnchor] = useState<MenuAnchorRect | null>(null);
99
+ const triggerRef = useRef<MenuMeasurable | null>(null);
100
+
101
+ const openRef = useRef(isOpen);
102
+ openRef.current = isOpen;
103
+
104
+ // Set by `open()` so the effect below can tell an open it asked for from one a
105
+ // controlling parent made by flipping `isOpen` — which still needs a measurement.
106
+ const isOpeningRef = useRef(false);
107
+
108
+ const measureTrigger = useCallback((then?: () => void) => {
109
+ const node = triggerRef.current;
110
+ if (!node || typeof node.measureInWindow !== "function") {
111
+ then?.();
112
+ return;
113
+ }
114
+
115
+ node.measureInWindow((x, y, width, height) => {
116
+ setAnchor({ height, width, x, y });
117
+ then?.();
118
+ });
119
+ }, []);
120
+
121
+ const open = useCallback(
122
+ (next?: MenuAnchorRect) => {
123
+ const show = () => {
124
+ if (openRef.current) return;
125
+ isOpeningRef.current = true;
126
+ setOpen(true);
127
+ };
128
+
129
+ if (next) {
130
+ setAnchor(next);
131
+ show();
132
+ return;
133
+ }
134
+
135
+ measureTrigger(show);
136
+ },
137
+ [measureTrigger, setOpen]
138
+ );
139
+
140
+ useEffect(() => {
141
+ if (!isOpen) return;
142
+ if (isOpeningRef.current) {
143
+ isOpeningRef.current = false;
144
+ return;
145
+ }
146
+ measureTrigger();
147
+ }, [isOpen, measureTrigger]);
148
+
149
+ const close = useCallback(() => {
150
+ if (openRef.current) setOpen(false);
151
+ }, [setOpen]);
152
+
153
+ const toggle = useCallback(() => {
154
+ if (openRef.current) close();
155
+ else open();
156
+ }, [close, open]);
157
+
158
+ return useMemo<MenuContextValue>(
159
+ () => ({ anchor, close, haptic, isOpen, open, toggle, triggerRef }),
160
+ [anchor, close, haptic, isOpen, open, toggle]
161
+ );
162
+ }
163
+
164
+ export type MenuSubContextValue = {
165
+ isOpen: boolean;
166
+ toggle: () => void;
167
+ /** The submenu's travel, 0 closed to 1 open. The chevron and the clip both read it. */
168
+ progress: SharedValue<number>;
169
+ contentHeight: SharedValue<number>;
170
+ onMeasured: () => void;
171
+ };
172
+
173
+ const MenuSubContext = createContext<MenuSubContextValue | null>(null);
174
+
175
+ export function MenuSubProvider({
176
+ value,
177
+ children,
178
+ }: {
179
+ value: MenuSubContextValue;
180
+ children: ReactNode;
181
+ }): ReactElement {
182
+ return <MenuSubContext value={value}>{children}</MenuSubContext>;
183
+ }
184
+ MenuSubProvider.displayName = "DelacourUI.Menu.SubProvider";
185
+
186
+ export function useMenuSubPart(part: string): MenuSubContextValue {
187
+ const context = use(MenuSubContext);
188
+ if (!context) throw new Error(`${part} must be used inside a <Menu.Sub>.`);
189
+ return context;
190
+ }
191
+
192
+ export type MenuRadioGroupContextValue = {
193
+ value: string | undefined;
194
+ select: (value: string) => void;
195
+ };
196
+
197
+ const MenuRadioGroupContext = createContext<MenuRadioGroupContextValue | null>(null);
198
+
199
+ export function MenuRadioGroupProvider({
200
+ value,
201
+ children,
202
+ }: {
203
+ value: MenuRadioGroupContextValue;
204
+ children: ReactNode;
205
+ }): ReactElement {
206
+ return <MenuRadioGroupContext value={value}>{children}</MenuRadioGroupContext>;
207
+ }
208
+ MenuRadioGroupProvider.displayName = "DelacourUI.Menu.RadioGroupProvider";
209
+
210
+ export function useMenuRadioGroupPart(part: string): MenuRadioGroupContextValue {
211
+ const context = use(MenuRadioGroupContext);
212
+ if (!context) throw new Error(`${part} must be used inside a <Menu.RadioGroup>.`);
213
+ return context;
214
+ }
@@ -0,0 +1,88 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import type { HapticFeedback } from "../pressable/pressable";
3
+ import { MenuRootProvider, useMenuRootValue } from "./menu.context";
4
+ import { MenuBackground } from "./menu-background";
5
+ import { MenuCheckboxItem } from "./menu-checkbox-item";
6
+ import { MenuContent } from "./menu-content";
7
+ import { MenuItem } from "./menu-item";
8
+ import { MenuLabel } from "./menu-label";
9
+ import { MenuRadioGroup } from "./menu-radio-group";
10
+ import { MenuRadioItem } from "./menu-radio-item";
11
+ import { MenuSeparator } from "./menu-separator";
12
+ import { MenuSub } from "./menu-sub";
13
+ import { MenuSubContent } from "./menu-sub-content";
14
+ import { MenuSubTrigger } from "./menu-sub-trigger";
15
+ import { MenuTrigger } from "./menu-trigger";
16
+
17
+ export type MenuProps = {
18
+ children: ReactNode;
19
+ /** Controlled open state. Leave it off and the menu holds its own. */
20
+ isOpen?: boolean;
21
+ /** Initial open state while uncontrolled. */
22
+ defaultOpen?: boolean;
23
+ onOpenChange?: (isOpen: boolean) => void;
24
+ /** Played when a row is chosen. Off by default. */
25
+ haptic?: false | HapticFeedback;
26
+ };
27
+
28
+ function MenuRoot({ children, isOpen, defaultOpen, onOpenChange, haptic }: MenuProps): ReactElement {
29
+ const value = useMenuRootValue({ defaultOpen, haptic, isOpen, onOpenChange });
30
+ return <MenuRootProvider value={value}>{children}</MenuRootProvider>;
31
+ }
32
+
33
+ /**
34
+ * A list of verbs dropped from the control that opens it — rename, duplicate,
35
+ * share, delete.
36
+ *
37
+ * A menu is not a select: a row runs an action, it does not become the
38
+ * trigger's value. The panel is anchored to the trigger, flips above it near
39
+ * the bottom of the screen, stays inside the safe area, and scrolls when the
40
+ * rows outgrow the room.
41
+ *
42
+ * Rows close the menu when chosen; `Menu.CheckboxItem` rows stay open, since
43
+ * several are usually toggled at once. `Menu.Sub` expands in place. An outside
44
+ * tap, Android's back button and a second press of the trigger all close it.
45
+ *
46
+ * State works either way from one hook: pass `isOpen` to control it, or leave
47
+ * it off. `onOpenChange` hears both. `useMenu()` hands a custom part
48
+ * `{ isOpen, open, close }`.
49
+ *
50
+ * @example
51
+ * <Menu>
52
+ * <Menu.Trigger asChild>
53
+ * <Button variant="outline">Options</Button>
54
+ * </Menu.Trigger>
55
+ * <Menu.Content>
56
+ * <Menu.Item icon={IconPencil} onSelect={rename}>Rename</Menu.Item>
57
+ * <Menu.Separator />
58
+ * <Menu.Item icon={IconTrashCan} variant="destructive" onSelect={remove}>Delete</Menu.Item>
59
+ * </Menu.Content>
60
+ * </Menu>
61
+ */
62
+ export const Menu = Object.assign(MenuRoot, {
63
+ /** The control that opens and closes the menu. `asChild` donates the press to a `Button`. */
64
+ Trigger: MenuTrigger,
65
+ /** The anchored panel, in a transparent `Modal`. */
66
+ Content: MenuContent,
67
+ /** The panel's surface. Pass one as a `Menu.Content` child to repaint it. */
68
+ Background: MenuBackground,
69
+ /** A section heading. */
70
+ Label: MenuLabel,
71
+ /** A verb: runs `onSelect` and closes. */
72
+ Item: MenuItem,
73
+ /** A row that toggles a setting. Stays open. */
74
+ CheckboxItem: MenuCheckboxItem,
75
+ /** Holds the value for a set of radio rows. */
76
+ RadioGroup: MenuRadioGroup,
77
+ /** One option in a radio group. Closes. */
78
+ RadioItem: MenuRadioItem,
79
+ /** A rule between groups of rows. */
80
+ Separator: MenuSeparator,
81
+ /** Nested rows that expand in place. */
82
+ Sub: MenuSub,
83
+ /** The row that expands a `Menu.Sub`. */
84
+ SubTrigger: MenuSubTrigger,
85
+ /** The rows a `Menu.Sub` discloses. */
86
+ SubContent: MenuSubContent,
87
+ displayName: "DelacourUI.Menu",
88
+ });
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ViewProps } from "react-native";
3
+ import type { IconComponent } from "../icon/icon";
4
+ import type { MenuItemVariant } from "./menu.variants";
5
+
6
+ /**
7
+ * What every row shares — an item, a checkbox row, a radio row and a sub-trigger.
8
+ *
9
+ * Built on `ViewProps` rather than `PressableProps`: a row drives its own press
10
+ * fill on a tap of its own, so `feedback`, `asChild` and the pressable's other
11
+ * knobs would be props it accepts and ignores.
12
+ */
13
+ export type MenuItemProps = Omit<ViewProps, "children" | "style"> & {
14
+ /** The label. A string is wrapped in a `Text` on the row's type. */
15
+ children: ReactNode;
16
+ className?: string;
17
+ icon?: IconComponent;
18
+ /** Which side the icon sits on. Default `leading`. */
19
+ iconPlacement?: "leading" | "trailing";
20
+ /** A second, quieter line under the label. */
21
+ description?: string;
22
+ /** A key hint drawn at the row's end, e.g. `⌘R`. */
23
+ shortcut?: string;
24
+ /** Anything else drawn at the row's end. */
25
+ trailing?: ReactNode;
26
+ variant?: MenuItemVariant;
27
+ /** Reserve the icon column with no icon, so the label lines up with rows that have one. */
28
+ isInset?: boolean;
29
+ isDisabled?: boolean;
30
+ /** Whether choosing the row closes the menu. Default `true`. */
31
+ isClosedOnSelect?: boolean;
32
+ onSelect?: () => void;
33
+ };