@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,182 @@
1
+ import { type ReactElement, type ReactNode, useCallback, useEffect, useMemo } from "react";
2
+ import { type AccessibilityActionEvent, View, type ViewProps, type ViewStyle } from "react-native";
3
+ import Animated, {
4
+ ReduceMotion,
5
+ useAnimatedStyle,
6
+ useReducedMotion,
7
+ useSharedValue,
8
+ withSpring,
9
+ withTiming,
10
+ } from "react-native-reanimated";
11
+ import { Pressable } from "../pressable";
12
+ import {
13
+ type SelectionModeItemContextValue,
14
+ SelectionModeItemProvider,
15
+ useSelectionModePart,
16
+ } from "./selection-mode.context";
17
+ import {
18
+ resolveIndicatorShown,
19
+ resolveItemAccessibility,
20
+ resolveItemPress,
21
+ SELECTION_MODE_INDICATOR_OFFSET,
22
+ SELECTION_MODE_MOTION,
23
+ type SelectionIndicator,
24
+ selectionModeVariants,
25
+ } from "./selection-mode.variants";
26
+ import { SelectionModeIndicator, SelectionModeRing } from "./selection-mode-indicator";
27
+
28
+ export type SelectionModeItemProps = Omit<ViewProps, "children"> & {
29
+ /** The item's id. Selection is a set of ids, never indices. */
30
+ value: string;
31
+ /** What it wraps — a row, an avatar, a swatch. */
32
+ children: ReactNode;
33
+ /** The item's own press, run while the mode is off — and always, for a disabled item. */
34
+ onPress?: () => void;
35
+ /** Cannot start the mode or be picked. Its own `onPress` still runs. */
36
+ isDisabled?: boolean;
37
+ /** How it shows it is picked. Default `leading`. */
38
+ indicator?: SelectionIndicator;
39
+ /** Show the indicator even while the mode is off — an always-on picker. */
40
+ isIndicatorAlwaysShown?: boolean;
41
+ className?: string;
42
+ /** Reshapes the `ring` to the item's own corner — `rounded-full` around a round swatch. */
43
+ ringClassName?: string;
44
+ };
45
+
46
+ /**
47
+ * Slides a `leading` indicator in: a spacer opens from 0 to 34pt and pushes the
48
+ * content over while the mark fades in. Under reduced motion the spacer snaps
49
+ * and only the fade plays, 150ms.
50
+ */
51
+ function useLeadingStyles(isShown: boolean): {
52
+ spacerStyle: ReturnType<typeof useAnimatedStyle<ViewStyle>>;
53
+ markStyle: ReturnType<typeof useAnimatedStyle<ViewStyle>>;
54
+ } {
55
+ const isReducedMotion = useReducedMotion();
56
+ const slide = useSharedValue(isShown ? 1 : 0);
57
+ const fade = useSharedValue(isShown ? 1 : 0);
58
+
59
+ useEffect(() => {
60
+ const target = isShown ? 1 : 0;
61
+ if (isReducedMotion) {
62
+ slide.value = target;
63
+ fade.value = withTiming(target, {
64
+ duration: SELECTION_MODE_MOTION.reducedFadeMs,
65
+ reduceMotion: ReduceMotion.Never,
66
+ });
67
+ return;
68
+ }
69
+ slide.value = withSpring(target, SELECTION_MODE_MOTION.spring);
70
+ fade.value = withTiming(target, { duration: SELECTION_MODE_MOTION.fadeMs });
71
+ }, [fade, isReducedMotion, isShown, slide]);
72
+
73
+ const offset = SELECTION_MODE_INDICATOR_OFFSET;
74
+ const spacerStyle = useAnimatedStyle(() => ({ width: slide.value * offset }));
75
+ const markStyle = useAnimatedStyle(() => ({ opacity: fade.value }));
76
+ return { markStyle, spacerStyle };
77
+ }
78
+
79
+ /**
80
+ * One pickable thing. Wraps whatever it holds rather than replacing it.
81
+ *
82
+ * Off, a press runs the item's own `onPress` and a long press turns the mode on
83
+ * with this item picked. On, a press toggles it and `onPress` does not run. A
84
+ * disabled item never toggles and never starts the mode, but keeps its own
85
+ * press — a section header, a "load more" row.
86
+ *
87
+ * While the mode is on it is announced as a checkbox; while off it keeps the
88
+ * semantics of what it wraps and offers "Start selecting" as an action.
89
+ */
90
+ export function SelectionModeItem({
91
+ value,
92
+ children,
93
+ onPress,
94
+ isDisabled = false,
95
+ indicator = "leading",
96
+ isIndicatorAlwaysShown = false,
97
+ className,
98
+ ringClassName,
99
+ onAccessibilityAction,
100
+ ...props
101
+ }: SelectionModeItemProps): ReactElement {
102
+ const selection = useSelectionModePart("SelectionMode.Item");
103
+ const { isActive, toggle, enter } = selection;
104
+ const isSelected = selection.isSelected(value);
105
+ const isShown = resolveIndicatorShown({ indicator, isActive, isIndicatorAlwaysShown });
106
+
107
+ const press = resolveItemPress({ hasOnPress: onPress !== undefined, isActive, isDisabled });
108
+ const pressKind = press.kind;
109
+ const handlePress = useCallback(() => {
110
+ if (pressKind === "toggle") toggle(value);
111
+ else if (pressKind === "press") onPress?.();
112
+ }, [onPress, pressKind, toggle, value]);
113
+
114
+ const canEnter = !isActive && !isDisabled;
115
+ const handleLongPress = useCallback(() => enter(value), [enter, value]);
116
+
117
+ const accessibility = resolveItemAccessibility({ isActive, isDisabled, isSelected });
118
+ const handleAccessibilityAction = useCallback(
119
+ (event: AccessibilityActionEvent) => {
120
+ if (canEnter && event.nativeEvent.actionName === "longpress") enter(value);
121
+ onAccessibilityAction?.(event);
122
+ },
123
+ [canEnter, enter, onAccessibilityAction, value]
124
+ );
125
+ const accessibilityProps =
126
+ accessibility.kind === "checkbox"
127
+ ? {
128
+ accessibilityHint: accessibility.accessibilityHint,
129
+ accessibilityRole: accessibility.accessibilityRole,
130
+ accessibilityState: accessibility.accessibilityState,
131
+ }
132
+ : {
133
+ accessibilityActions: accessibility.accessibilityActions,
134
+ accessibilityRole: onPress ? ("button" as const) : undefined,
135
+ };
136
+
137
+ const context = useMemo<SelectionModeItemContextValue>(
138
+ () => ({ indicator, isDisabled, isSelected, value }),
139
+ [indicator, isDisabled, isSelected, value]
140
+ );
141
+
142
+ const slots = selectionModeVariants({ indicator, isSelected: isSelected && isShown });
143
+ const { markStyle, spacerStyle } = useLeadingStyles(isShown);
144
+
145
+ // A leading item is a row: a spacer that opens to push the content over, the
146
+ // content, then the mark drawn on top of the gap the spacer made. The mark
147
+ // comes last so a child with a fill of its own never paints over it.
148
+ const content =
149
+ indicator === "leading" ? (
150
+ <>
151
+ <Animated.View style={spacerStyle} />
152
+ <View className="flex-1">{children}</View>
153
+ <Animated.View className={slots.indicatorSlot()} pointerEvents="none" style={markStyle}>
154
+ <SelectionModeIndicator />
155
+ </Animated.View>
156
+ </>
157
+ ) : (
158
+ <>
159
+ {children}
160
+ {indicator === "ring" && isShown && isSelected ? (
161
+ <SelectionModeRing className={ringClassName} isSelected={isSelected} />
162
+ ) : null}
163
+ </>
164
+ );
165
+
166
+ return (
167
+ <SelectionModeItemProvider value={context}>
168
+ <Pressable
169
+ className={slots.item({ className })}
170
+ feedback={indicator === "leading" ? "fade" : "scale"}
171
+ onAccessibilityAction={handleAccessibilityAction}
172
+ onLongPress={canEnter ? handleLongPress : undefined}
173
+ onPress={pressKind === "none" ? undefined : handlePress}
174
+ {...accessibilityProps}
175
+ {...props}
176
+ >
177
+ {content}
178
+ </Pressable>
179
+ </SelectionModeItemProvider>
180
+ );
181
+ }
182
+ SelectionModeItem.displayName = "DelacourUI.SelectionMode.Item";
@@ -0,0 +1,89 @@
1
+ import { createContext, type ReactElement, type ReactNode, use } from "react";
2
+ import type { SelectionModeState } from "./selection-mode.types";
3
+ import type { SelectionIndicator } from "./selection-mode.variants";
4
+
5
+ export type SelectionModeContextValue = SelectionModeState;
6
+
7
+ const SelectionModeContext = createContext<SelectionModeContextValue | null>(null);
8
+
9
+ /**
10
+ * Supplies a selection to every part beneath it.
11
+ *
12
+ * In a leaf of its own, importing nothing but types, so a part can read it
13
+ * without importing `./selection-mode` and closing a cycle (package AGENTS.md
14
+ * rule 3).
15
+ */
16
+ export function SelectionModeProvider({
17
+ value,
18
+ children,
19
+ }: {
20
+ value: SelectionModeContextValue;
21
+ children: ReactNode;
22
+ }): ReactElement {
23
+ return <SelectionModeContext value={value}>{children}</SelectionModeContext>;
24
+ }
25
+ SelectionModeProvider.displayName = "DelacourUI.SelectionMode.Provider";
26
+
27
+ /** The enclosing selection, or null outside a `<SelectionMode>`. */
28
+ export function useSelectionModeContext(): SelectionModeContextValue | null {
29
+ return use(SelectionModeContext);
30
+ }
31
+
32
+ /**
33
+ * Reads the enclosing selection: whether the mode is on, what is picked, and
34
+ * the actions that move it.
35
+ *
36
+ * For a custom child — an item drawn with `indicator="none"` that paints its own
37
+ * picked state, a toolbar of its own. Throws outside a `<SelectionMode>` — use
38
+ * {@link useSelectionModeContext} where the selection is optional.
39
+ */
40
+ export function useSelectionMode(): SelectionModeContextValue {
41
+ const context = useSelectionModeContext();
42
+ if (!context) {
43
+ throw new Error("useSelectionMode must be called inside a <SelectionMode>.");
44
+ }
45
+ return context;
46
+ }
47
+
48
+ /**
49
+ * The enclosing selection, for a compound part that cannot work without it.
50
+ *
51
+ * Internal: deliberately not re-exported from `index.ts`.
52
+ */
53
+ export function useSelectionModePart(component: string): SelectionModeContextValue {
54
+ const context = useSelectionModeContext();
55
+ if (!context) {
56
+ throw new Error(`${component} must be rendered inside a <SelectionMode>.`);
57
+ }
58
+ return context;
59
+ }
60
+
61
+ export type SelectionModeItemContextValue = {
62
+ /** The item's id. */
63
+ value: string;
64
+ /** Whether it is picked. */
65
+ isSelected: boolean;
66
+ /** Whether it can be picked at all. */
67
+ isDisabled: boolean;
68
+ /** How it shows it is picked. */
69
+ indicator: SelectionIndicator;
70
+ };
71
+
72
+ const SelectionModeItemContext = createContext<SelectionModeItemContextValue | null>(null);
73
+
74
+ /** Supplies one item's id and state, so a `SelectionMode.Indicator` inside it needs no `value`. */
75
+ export function SelectionModeItemProvider({
76
+ value,
77
+ children,
78
+ }: {
79
+ value: SelectionModeItemContextValue;
80
+ children: ReactNode;
81
+ }): ReactElement {
82
+ return <SelectionModeItemContext value={value}>{children}</SelectionModeItemContext>;
83
+ }
84
+ SelectionModeItemProvider.displayName = "DelacourUI.SelectionMode.Item.Provider";
85
+
86
+ /** The enclosing item, or null outside a `<SelectionMode.Item>`. */
87
+ export function useSelectionModeItemContext(): SelectionModeItemContextValue | null {
88
+ return use(SelectionModeItemContext);
89
+ }
@@ -0,0 +1,207 @@
1
+ import { type ReactElement, type ReactNode, useCallback, useMemo } from "react";
2
+ import { View, type ViewProps } from "react-native";
3
+ import { useControllableState } from "../../hooks/use-controllable-state";
4
+ import { type HapticFeedback, playHaptic } from "../pressable/pressable";
5
+ import { type SelectionModeContextValue, SelectionModeProvider } from "./selection-mode.context";
6
+ import {
7
+ resolveIsAllSelected,
8
+ resolveIsSameSelection,
9
+ resolveSelectAll,
10
+ resolveToggle,
11
+ selectionModeVariants,
12
+ } from "./selection-mode.variants";
13
+ import { SelectionModeAction } from "./selection-mode-action";
14
+ import { SelectionModeBar } from "./selection-mode-bar";
15
+ import { SelectionModeGroup } from "./selection-mode-group";
16
+ import { SelectionModeHeader } from "./selection-mode-header";
17
+ import { SelectionModeIndicator } from "./selection-mode-indicator";
18
+ import { SelectionModeItem } from "./selection-mode-item";
19
+
20
+ export type SelectionModeProps = ViewProps & {
21
+ children: ReactNode;
22
+ className?: string;
23
+ /** Every pickable id, in list order — what select-all picks and "n of m" counts. */
24
+ values?: readonly string[];
25
+ /** Whether the mode is on. Controlled. */
26
+ isActive?: boolean;
27
+ /** Whether the mode starts on, while uncontrolled. */
28
+ defaultActive?: boolean;
29
+ onActiveChange?: (isActive: boolean) => void;
30
+ /** The picked ids, in the order they were picked. Controlled. */
31
+ selected?: readonly string[];
32
+ /** The ids picked to begin with, while uncontrolled. */
33
+ defaultSelected?: readonly string[];
34
+ /** Called with the whole new list. Never called for a change that changes nothing. */
35
+ onSelectedChange?: (selected: string[]) => void;
36
+ /** The most ids that can be picked. Select-all stops here too. */
37
+ max?: number;
38
+ /** Played on entering the mode and on every toggle. Off by default. */
39
+ haptic?: false | HapticFeedback;
40
+ };
41
+
42
+ function SelectionModeRoot({
43
+ children,
44
+ className,
45
+ values,
46
+ isActive,
47
+ defaultActive = false,
48
+ onActiveChange,
49
+ selected,
50
+ defaultSelected = EMPTY,
51
+ onSelectedChange,
52
+ max,
53
+ haptic = false,
54
+ ...props
55
+ }: SelectionModeProps): ReactElement {
56
+ const [active, setActive] = useControllableState<boolean>({
57
+ defaultValue: defaultActive,
58
+ onChange: onActiveChange,
59
+ value: isActive,
60
+ });
61
+ const [picked, setPicked] = useControllableState<readonly string[]>({
62
+ defaultValue: defaultSelected,
63
+ onChange: onSelectedChange as ((next: readonly string[]) => void) | undefined,
64
+ value: selected,
65
+ });
66
+ const isSelectedControlled = selected !== undefined;
67
+
68
+ const commit = useCallback(
69
+ (next: string[]): boolean => {
70
+ if (resolveIsSameSelection(next, picked)) return false;
71
+ setPicked(next);
72
+ return true;
73
+ },
74
+ [picked, setPicked]
75
+ );
76
+
77
+ const toggle = useCallback(
78
+ (value: string) => {
79
+ if (commit(resolveToggle({ max, selected: picked, value })) && haptic) playHaptic(haptic);
80
+ },
81
+ [commit, haptic, max, picked]
82
+ );
83
+
84
+ const enter = useCallback(
85
+ (value?: string) => {
86
+ if (!active) setActive(true);
87
+ const didPick =
88
+ value !== undefined && !picked.includes(value) && commit(resolveToggle({ max, selected: picked, value }));
89
+ if (haptic && (!active || didPick)) playHaptic(haptic);
90
+ },
91
+ [active, commit, haptic, max, picked, setActive]
92
+ );
93
+
94
+ const exit = useCallback(() => {
95
+ if (active) setActive(false);
96
+ if (!isSelectedControlled) commit([]);
97
+ }, [active, commit, isSelectedControlled, setActive]);
98
+
99
+ const selectAll = useCallback(() => {
100
+ commit(resolveSelectAll({ max, selected: picked, values: values ?? EMPTY }));
101
+ }, [commit, max, picked, values]);
102
+
103
+ const clear = useCallback(() => {
104
+ commit([]);
105
+ }, [commit]);
106
+
107
+ const isSelected = useCallback((value: string) => picked.includes(value), [picked]);
108
+
109
+ const context = useMemo<SelectionModeContextValue>(
110
+ () => ({
111
+ clear,
112
+ count: picked.length,
113
+ enter,
114
+ exit,
115
+ haptic,
116
+ isActive: active,
117
+ isAllSelected: resolveIsAllSelected({ max, selected: picked, values: values ?? EMPTY }),
118
+ isSelected,
119
+ max,
120
+ selectAll,
121
+ selected: [...picked],
122
+ toggle,
123
+ total: values?.length,
124
+ }),
125
+ [active, clear, enter, exit, haptic, isSelected, max, picked, selectAll, toggle, values]
126
+ );
127
+
128
+ return (
129
+ <SelectionModeProvider value={context}>
130
+ <View className={selectionModeVariants().root({ className })} {...props}>
131
+ {children}
132
+ </View>
133
+ </SelectionModeProvider>
134
+ );
135
+ }
136
+
137
+ /**
138
+ * A stable empty list, so an uncontrolled selection does not seed its state
139
+ * from a fresh array on every render.
140
+ */
141
+ const EMPTY: readonly string[] = [];
142
+
143
+ /**
144
+ * Pick several things at once, then act on them from a bar.
145
+ *
146
+ * A mode: off, the list is for reading and each item keeps its own press; a
147
+ * long press on an item turns the mode on with that item picked. On, a press
148
+ * toggles, `SelectionMode.Header` says so and offers the way out, and
149
+ * `SelectionMode.Bar` rises over the bottom edge with the actions.
150
+ *
151
+ * Selection is a set of ids — a `string[]` in the order things were picked,
152
+ * never indices — controlled with `selected` + `onSelectedChange` or held from
153
+ * `defaultSelected`. The mode is `isActive` + `onActiveChange`, or
154
+ * `defaultActive`. Exiting clears an uncontrolled selection; a controlled one is
155
+ * left to the caller. `values` lists every pickable id, which is what
156
+ * select-all and the "n of m" count need. `max` caps both toggling and
157
+ * select-all.
158
+ *
159
+ * The root fills the height it is offered (`flex-1`), so give it one. The bar is
160
+ * absolute, over the list, so pad the bottom of the list (`pb-24`) for the last
161
+ * row to scroll clear of it.
162
+ *
163
+ * @example
164
+ * <SelectionMode onSelectedChange={setSelected} selected={selected} values={ids}>
165
+ * <SelectionMode.Header title="Choose" />
166
+ * <FlatList
167
+ * contentContainerClassName="pb-24"
168
+ * data={messages}
169
+ * renderItem={({ item }) => (
170
+ * <SelectionMode.Item onPress={() => open(item)} value={item.id}>
171
+ * <Item>…</Item>
172
+ * </SelectionMode.Item>
173
+ * )}
174
+ * />
175
+ * <SelectionMode.Bar>
176
+ * <SelectionMode.Action icon={IconTrashCan} isDestructive isExitOnPress onPress={remove}>
177
+ * Delete
178
+ * </SelectionMode.Action>
179
+ * </SelectionMode.Bar>
180
+ * </SelectionMode>
181
+ *
182
+ * @example
183
+ * <SelectionMode defaultActive values={swatches}>
184
+ * <SelectionMode.Group columns={5}>
185
+ * {swatches.map((swatch) => (
186
+ * <SelectionMode.Item indicator="ring" key={swatch} ringClassName="rounded-full" value={swatch}>
187
+ * <Swatch color={swatch} />
188
+ * </SelectionMode.Item>
189
+ * ))}
190
+ * </SelectionMode.Group>
191
+ * </SelectionMode>
192
+ */
193
+ export const SelectionMode = Object.assign(SelectionModeRoot, {
194
+ /** One pickable thing. Wraps whatever it holds. */
195
+ Item: SelectionModeItem,
196
+ /** The round picked mark, for an item that places its own. */
197
+ Indicator: SelectionModeIndicator,
198
+ /** Shown while the mode is on: close, the count and select-all. */
199
+ Header: SelectionModeHeader,
200
+ /** The actions, over the bottom edge, while something is picked. */
201
+ Bar: SelectionModeBar,
202
+ /** One action in the bar: an icon over a label. */
203
+ Action: SelectionModeAction,
204
+ /** Lays items out as a stacked card, a grid or a horizontal strip. */
205
+ Group: SelectionModeGroup,
206
+ displayName: "DelacourUI.SelectionMode",
207
+ });
@@ -0,0 +1,36 @@
1
+ import type { HapticFeedback } from "../pressable/pressable";
2
+
3
+ /**
4
+ * Everything a selection exposes — what the root publishes and what
5
+ * `useSelectionMode()` returns.
6
+ *
7
+ * Shared by the root, which builds it, and the context leaf, which carries it.
8
+ */
9
+ export type SelectionModeState = {
10
+ /** Whether the mode is on: a press toggles, the header and bar show. */
11
+ isActive: boolean;
12
+ /** The picked ids, in the order they were picked. */
13
+ selected: string[];
14
+ /** Whether `value` is picked. */
15
+ isSelected: (value: string) => boolean;
16
+ /** Picks or unpicks `value`, respecting `max`. */
17
+ toggle: (value: string) => void;
18
+ /** Picks every value up to `max`, keeping existing picks first. */
19
+ selectAll: () => void;
20
+ /** Unpicks everything, and stays in the mode. */
21
+ clear: () => void;
22
+ /** Turns the mode on, picking `value` when one is given. */
23
+ enter: (value?: string) => void;
24
+ /** Turns the mode off. Clears an uncontrolled selection; a controlled one is left to the caller. */
25
+ exit: () => void;
26
+ /** How many ids are picked. */
27
+ count: number;
28
+ /** How many ids are pickable, or `undefined` when the root was given no `values`. */
29
+ total: number | undefined;
30
+ /** The cap on picks, if any. */
31
+ max: number | undefined;
32
+ /** Whether select-all has nothing left to add — the control reads "Deselect all". */
33
+ isAllSelected: boolean;
34
+ /** Played on enter and on every toggle. */
35
+ haptic: false | HapticFeedback;
36
+ };