@hzblj/kara-components 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +31 -3
  3. package/lib/module/atoms/activity-indicator/activity-indicator.android.js +42 -0
  4. package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -0
  5. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +19 -0
  6. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -0
  7. package/lib/module/atoms/activity-indicator/activity-indicator.types.js +4 -0
  8. package/lib/module/atoms/activity-indicator/activity-indicator.types.js.map +1 -0
  9. package/lib/module/atoms/activity-indicator/index.js +2 -1
  10. package/lib/module/atoms/activity-indicator/index.js.map +1 -1
  11. package/lib/module/atoms/avatar/avatar.js +2 -4
  12. package/lib/module/atoms/avatar/avatar.js.map +1 -1
  13. package/lib/module/atoms/blur-view/blur-view.android.js +100 -0
  14. package/lib/module/atoms/blur-view/blur-view.android.js.map +1 -0
  15. package/lib/module/atoms/blur-view/blur-view.ios.js +43 -0
  16. package/lib/module/atoms/blur-view/blur-view.ios.js.map +1 -0
  17. package/lib/module/atoms/blur-view/blur-view.types.js +4 -0
  18. package/lib/module/atoms/blur-view/blur-view.types.js.map +1 -0
  19. package/lib/module/atoms/blur-view/index.js +5 -0
  20. package/lib/module/atoms/blur-view/index.js.map +1 -0
  21. package/lib/module/atoms/checkbox/checkbox.android.js +7 -3
  22. package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
  23. package/lib/module/atoms/date-picker/date-picker.ios.js +32 -11
  24. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
  25. package/lib/module/atoms/glass-view/glass-view.android.js +36 -5
  26. package/lib/module/atoms/glass-view/glass-view.android.js.map +1 -1
  27. package/lib/module/atoms/image/image.js +13 -6
  28. package/lib/module/atoms/image/image.js.map +1 -1
  29. package/lib/module/atoms/image/image.types.js +4 -0
  30. package/lib/module/atoms/image/image.types.js.map +1 -0
  31. package/lib/module/atoms/image/index.js +1 -0
  32. package/lib/module/atoms/image/index.js.map +1 -1
  33. package/lib/module/atoms/index.js +1 -1
  34. package/lib/module/atoms/index.js.map +1 -1
  35. package/lib/module/atoms/island/island.android.js +6 -2
  36. package/lib/module/atoms/island/island.android.js.map +1 -1
  37. package/lib/module/atoms/island/island.ios.js +5 -2
  38. package/lib/module/atoms/island/island.ios.js.map +1 -1
  39. package/lib/module/atoms/pressable/pressable.js +38 -6
  40. package/lib/module/atoms/pressable/pressable.js.map +1 -1
  41. package/lib/module/atoms/rn-flat-list/rn-flat-list.js +10 -9
  42. package/lib/module/atoms/rn-flat-list/rn-flat-list.js.map +1 -1
  43. package/lib/module/atoms/rn-section-list/rn-section-list.js +5 -8
  44. package/lib/module/atoms/rn-section-list/rn-section-list.js.map +1 -1
  45. package/lib/module/atoms/rn-text/rn-text.js +5 -2
  46. package/lib/module/atoms/rn-text/rn-text.js.map +1 -1
  47. package/lib/module/atoms/segmented-control/index.js +1 -1
  48. package/lib/module/atoms/segmented-control/index.js.map +1 -1
  49. package/lib/module/atoms/segmented-control/segmented-control.android.js +74 -0
  50. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -0
  51. package/lib/module/atoms/segmented-control/segmented-control.ios.js +44 -0
  52. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -0
  53. package/lib/module/atoms/skeleton/skeleton.js +5 -5
  54. package/lib/module/atoms/skeleton/skeleton.js.map +1 -1
  55. package/lib/module/atoms/switch/switch.android.js +4 -1
  56. package/lib/module/atoms/switch/switch.android.js.map +1 -1
  57. package/lib/module/atoms/text/text.android.js +5 -2
  58. package/lib/module/atoms/text/text.android.js.map +1 -1
  59. package/lib/module/atoms/text/text.ios.js +14 -3
  60. package/lib/module/atoms/text/text.ios.js.map +1 -1
  61. package/lib/module/molecules/collapsible/collapsible.android.js +37 -7
  62. package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
  63. package/lib/module/molecules/navigation-bar/navigation-bar.android.js +2 -8
  64. package/lib/module/molecules/navigation-bar/navigation-bar.android.js.map +1 -1
  65. package/lib/module/molecules/pager/pager.android.js +5 -4
  66. package/lib/module/molecules/pager/pager.android.js.map +1 -1
  67. package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js +114 -11
  68. package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js.map +1 -1
  69. package/lib/module/molecules/search-bar/search-bar.js +15 -2
  70. package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
  71. package/lib/module/molecules/stepper/stepper.ios.js +45 -8
  72. package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
  73. package/lib/module/molecules/tooltip/tooltip.android.js +16 -5
  74. package/lib/module/molecules/tooltip/tooltip.android.js.map +1 -1
  75. package/lib/module/organisms/action-sheet/action-sheet.android.js +41 -20
  76. package/lib/module/organisms/action-sheet/action-sheet.android.js.map +1 -1
  77. package/lib/module/organisms/action-sheet/action-sheet.ios.js +24 -8
  78. package/lib/module/organisms/action-sheet/action-sheet.ios.js.map +1 -1
  79. package/lib/module/organisms/alert/alert.ios.js +15 -2
  80. package/lib/module/organisms/alert/alert.ios.js.map +1 -1
  81. package/lib/module/organisms/form/form.android.js +22 -9
  82. package/lib/module/organisms/form/form.android.js.map +1 -1
  83. package/lib/module/organisms/swipe-actions/swipe-actions.android.js +125 -11
  84. package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
  85. package/lib/module/organisms/swipe-actions/swipe-actions.ios.js +46 -18
  86. package/lib/module/organisms/swipe-actions/swipe-actions.ios.js.map +1 -1
  87. package/lib/module/ripple-clip.js +9 -1
  88. package/lib/module/ripple-clip.js.map +1 -1
  89. package/lib/module/templates/screen/screen.android.js +31 -10
  90. package/lib/module/templates/screen/screen.android.js.map +1 -1
  91. package/lib/typescript/atoms/activity-indicator/activity-indicator.d.ts +1 -3
  92. package/lib/typescript/atoms/activity-indicator/activity-indicator.ios.d.ts +3 -0
  93. package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +17 -0
  94. package/lib/typescript/atoms/activity-indicator/index.d.ts +1 -0
  95. package/lib/typescript/atoms/avatar/avatar.types.d.ts +5 -0
  96. package/lib/typescript/atoms/badge/badge.types.d.ts +3 -0
  97. package/lib/typescript/atoms/blur-view/blur-view.d.ts +1 -0
  98. package/lib/typescript/atoms/blur-view/blur-view.ios.d.ts +3 -0
  99. package/lib/typescript/atoms/blur-view/blur-view.types.d.ts +31 -0
  100. package/lib/typescript/atoms/blur-view/index.d.ts +2 -0
  101. package/lib/typescript/atoms/box/a-box.d.ts +1 -1
  102. package/lib/typescript/atoms/button/button.types.d.ts +12 -0
  103. package/lib/typescript/atoms/card/card.types.d.ts +6 -0
  104. package/lib/typescript/atoms/checkbox/checkbox.types.d.ts +5 -0
  105. package/lib/typescript/atoms/chip/chip.types.d.ts +5 -0
  106. package/lib/typescript/atoms/color-picker/color-picker.types.d.ts +4 -0
  107. package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +6 -1
  108. package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +14 -0
  109. package/lib/typescript/atoms/divider/divider.types.d.ts +3 -0
  110. package/lib/typescript/atoms/fab/fab.types.d.ts +7 -0
  111. package/lib/typescript/atoms/glass-view/glass-view.types.d.ts +13 -5
  112. package/lib/typescript/atoms/h-stack/h-stack.types.d.ts +6 -0
  113. package/lib/typescript/atoms/icon/icon.types.d.ts +8 -0
  114. package/lib/typescript/atoms/icon-button/icon-button.types.d.ts +5 -0
  115. package/lib/typescript/atoms/image/image.d.ts +12 -4
  116. package/lib/typescript/atoms/image/image.types.d.ts +4 -0
  117. package/lib/typescript/atoms/image/index.d.ts +1 -0
  118. package/lib/typescript/atoms/index.d.ts +1 -1
  119. package/lib/typescript/atoms/island/island.types.d.ts +10 -2
  120. package/lib/typescript/atoms/list/list.types.d.ts +2 -0
  121. package/lib/typescript/atoms/picker/picker.types.d.ts +12 -0
  122. package/lib/typescript/atoms/pressable/pressable.types.d.ts +10 -0
  123. package/lib/typescript/atoms/progress/progress.types.d.ts +6 -0
  124. package/lib/typescript/atoms/rn-flat-list/rn-flat-list.d.ts +5 -8
  125. package/lib/typescript/atoms/rn-flat-list/rn-flat-list.types.d.ts +3 -1
  126. package/lib/typescript/atoms/rn-scroll-view/rn-scroll-view.types.d.ts +4 -0
  127. package/lib/typescript/atoms/rn-section-list/rn-section-list.d.ts +2 -8
  128. package/lib/typescript/atoms/rn-section-list/rn-section-list.types.d.ts +3 -1
  129. package/lib/typescript/atoms/rn-text/rn-text.types.d.ts +10 -1
  130. package/lib/typescript/atoms/rn-view/rn-view.types.d.ts +2 -0
  131. package/lib/typescript/atoms/scroll-view/scroll-view.types.d.ts +4 -0
  132. package/lib/typescript/atoms/segmented-control/segmented-control.d.ts +1 -6
  133. package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +7 -0
  134. package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +13 -0
  135. package/lib/typescript/atoms/skeleton/skeleton.types.d.ts +11 -2
  136. package/lib/typescript/atoms/slider/slider.types.d.ts +7 -0
  137. package/lib/typescript/atoms/spacer/spacer.types.d.ts +3 -0
  138. package/lib/typescript/atoms/surface/surface.types.d.ts +7 -0
  139. package/lib/typescript/atoms/switch/switch.types.d.ts +8 -0
  140. package/lib/typescript/atoms/text/text.types.d.ts +18 -1
  141. package/lib/typescript/atoms/text-field/text-field.types.d.ts +12 -0
  142. package/lib/typescript/atoms/v-stack/v-stack.types.d.ts +9 -0
  143. package/lib/typescript/atoms/z-stack/z-stack.types.d.ts +8 -0
  144. package/lib/typescript/molecules/collapsible/collapsible.types.d.ts +8 -0
  145. package/lib/typescript/molecules/empty-state/empty-state.types.d.ts +5 -0
  146. package/lib/typescript/molecules/label/label.types.d.ts +6 -0
  147. package/lib/typescript/molecules/labeled-content/labeled-content.types.d.ts +6 -0
  148. package/lib/typescript/molecules/link/link.types.d.ts +6 -0
  149. package/lib/typescript/molecules/list-item/list-item.types.d.ts +9 -0
  150. package/lib/typescript/molecules/navigation-bar/navigation-bar.types.d.ts +8 -0
  151. package/lib/typescript/molecules/pager/pager.types.d.ts +2 -0
  152. package/lib/typescript/molecules/radio-group/radio-group.types.d.ts +10 -0
  153. package/lib/typescript/molecules/rn-list-item/rn-list-item.types.d.ts +1 -1
  154. package/lib/typescript/molecules/rn-tooltip/rn-tooltip.types.d.ts +11 -0
  155. package/lib/typescript/molecules/scroll-edge-container/scroll-edge-container.types.d.ts +7 -2
  156. package/lib/typescript/molecules/search-bar/search-bar.types.d.ts +8 -0
  157. package/lib/typescript/molecules/stepper/stepper.ios.d.ts +5 -0
  158. package/lib/typescript/molecules/stepper/stepper.types.d.ts +10 -0
  159. package/lib/typescript/molecules/tooltip/tooltip.types.d.ts +12 -0
  160. package/lib/typescript/organisms/action-sheet/action-sheet.types.d.ts +8 -0
  161. package/lib/typescript/organisms/alert/alert.types.d.ts +11 -0
  162. package/lib/typescript/organisms/context-menu/context-menu.types.d.ts +4 -0
  163. package/lib/typescript/organisms/fab-menu/fab-menu.types.d.ts +2 -0
  164. package/lib/typescript/organisms/form/form.types.d.ts +6 -0
  165. package/lib/typescript/organisms/menu/menu.types.d.ts +17 -0
  166. package/lib/typescript/organisms/sheet/sheet.types.d.ts +19 -0
  167. package/lib/typescript/organisms/snackbar/snackbar.types.d.ts +8 -0
  168. package/lib/typescript/organisms/swipe-actions/swipe-actions.types.d.ts +36 -13
  169. package/lib/typescript/templates/screen/screen.types.d.ts +9 -0
  170. package/package.json +5 -7
  171. package/src/atoms/activity-indicator/activity-indicator.android.tsx +32 -0
  172. package/src/atoms/activity-indicator/activity-indicator.ios.tsx +15 -0
  173. package/src/atoms/activity-indicator/activity-indicator.types.ts +20 -0
  174. package/src/atoms/activity-indicator/index.ts +1 -0
  175. package/src/atoms/avatar/avatar.tsx +1 -1
  176. package/src/atoms/avatar/avatar.types.ts +5 -0
  177. package/src/atoms/badge/badge.types.ts +3 -0
  178. package/src/atoms/blur-view/blur-view.android.tsx +68 -0
  179. package/src/atoms/blur-view/blur-view.ios.tsx +37 -0
  180. package/src/atoms/blur-view/blur-view.types.ts +34 -0
  181. package/src/atoms/blur-view/index.ts +2 -0
  182. package/src/atoms/button/button.types.ts +12 -0
  183. package/src/atoms/card/card.types.ts +6 -0
  184. package/src/atoms/checkbox/checkbox.android.tsx +7 -3
  185. package/src/atoms/checkbox/checkbox.types.ts +5 -0
  186. package/src/atoms/chip/chip.types.ts +5 -0
  187. package/src/atoms/color-picker/color-picker.types.ts +4 -0
  188. package/src/atoms/date-picker/date-picker.ios.tsx +33 -13
  189. package/src/atoms/date-picker/date-picker.types.ts +14 -0
  190. package/src/atoms/divider/divider.types.ts +3 -0
  191. package/src/atoms/fab/fab.types.ts +7 -0
  192. package/src/atoms/glass-view/glass-view.android.tsx +41 -3
  193. package/src/atoms/glass-view/glass-view.types.ts +13 -5
  194. package/src/atoms/h-stack/h-stack.types.ts +6 -0
  195. package/src/atoms/icon/icon.types.ts +8 -0
  196. package/src/atoms/icon-button/icon-button.types.ts +5 -0
  197. package/src/atoms/image/image.tsx +11 -12
  198. package/src/atoms/image/image.types.ts +6 -0
  199. package/src/atoms/image/index.ts +1 -0
  200. package/src/atoms/index.ts +1 -1
  201. package/src/atoms/island/island.android.tsx +10 -2
  202. package/src/atoms/island/island.ios.tsx +4 -2
  203. package/src/atoms/island/island.types.ts +10 -2
  204. package/src/atoms/list/list.types.ts +2 -0
  205. package/src/atoms/picker/picker.types.ts +12 -0
  206. package/src/atoms/pressable/pressable.tsx +46 -6
  207. package/src/atoms/pressable/pressable.types.ts +10 -0
  208. package/src/atoms/progress/progress.types.ts +6 -0
  209. package/src/atoms/rn-flat-list/rn-flat-list.tsx +8 -8
  210. package/src/atoms/rn-flat-list/rn-flat-list.types.ts +3 -1
  211. package/src/atoms/rn-scroll-view/rn-scroll-view.types.ts +4 -0
  212. package/src/atoms/rn-section-list/rn-section-list.tsx +5 -8
  213. package/src/atoms/rn-section-list/rn-section-list.types.ts +3 -1
  214. package/src/atoms/rn-text/rn-text.tsx +6 -3
  215. package/src/atoms/rn-text/rn-text.types.ts +15 -5
  216. package/src/atoms/rn-view/rn-view.types.ts +2 -0
  217. package/src/atoms/scroll-view/scroll-view.types.ts +4 -0
  218. package/src/atoms/segmented-control/segmented-control.android.tsx +79 -0
  219. package/src/atoms/segmented-control/segmented-control.ios.tsx +47 -0
  220. package/src/atoms/segmented-control/segmented-control.types.ts +13 -0
  221. package/src/atoms/skeleton/skeleton.tsx +4 -4
  222. package/src/atoms/skeleton/skeleton.types.ts +11 -2
  223. package/src/atoms/slider/slider.types.ts +7 -0
  224. package/src/atoms/spacer/spacer.types.ts +3 -0
  225. package/src/atoms/surface/surface.types.ts +7 -0
  226. package/src/atoms/switch/switch.android.tsx +4 -1
  227. package/src/atoms/switch/switch.types.ts +8 -0
  228. package/src/atoms/text/text.android.tsx +7 -1
  229. package/src/atoms/text/text.ios.tsx +14 -3
  230. package/src/atoms/text/text.types.ts +18 -1
  231. package/src/atoms/text-field/text-field.types.ts +12 -0
  232. package/src/atoms/v-stack/v-stack.types.ts +9 -0
  233. package/src/atoms/z-stack/z-stack.types.ts +8 -0
  234. package/src/molecules/collapsible/collapsible.android.tsx +47 -7
  235. package/src/molecules/collapsible/collapsible.types.ts +8 -0
  236. package/src/molecules/empty-state/empty-state.types.ts +5 -0
  237. package/src/molecules/label/label.types.ts +6 -0
  238. package/src/molecules/labeled-content/labeled-content.types.ts +6 -0
  239. package/src/molecules/link/link.types.ts +6 -0
  240. package/src/molecules/list-item/list-item.types.ts +9 -0
  241. package/src/molecules/navigation-bar/navigation-bar.android.tsx +1 -6
  242. package/src/molecules/navigation-bar/navigation-bar.types.ts +8 -0
  243. package/src/molecules/pager/pager.android.tsx +4 -1
  244. package/src/molecules/pager/pager.types.ts +2 -0
  245. package/src/molecules/radio-group/radio-group.types.ts +10 -0
  246. package/src/molecules/rn-list-item/rn-list-item.types.ts +1 -1
  247. package/src/molecules/rn-tooltip/rn-tooltip.types.ts +11 -0
  248. package/src/molecules/scroll-edge-container/scroll-edge-container.android.tsx +103 -10
  249. package/src/molecules/scroll-edge-container/scroll-edge-container.types.ts +7 -2
  250. package/src/molecules/search-bar/search-bar.tsx +9 -2
  251. package/src/molecules/search-bar/search-bar.types.ts +8 -0
  252. package/src/molecules/stepper/stepper.ios.tsx +37 -1
  253. package/src/molecules/stepper/stepper.types.ts +10 -0
  254. package/src/molecules/tooltip/tooltip.android.tsx +17 -9
  255. package/src/molecules/tooltip/tooltip.types.ts +12 -0
  256. package/src/organisms/action-sheet/action-sheet.android.tsx +37 -19
  257. package/src/organisms/action-sheet/action-sheet.ios.tsx +22 -9
  258. package/src/organisms/action-sheet/action-sheet.types.ts +8 -0
  259. package/src/organisms/alert/alert.ios.tsx +11 -1
  260. package/src/organisms/alert/alert.types.ts +11 -0
  261. package/src/organisms/context-menu/context-menu.types.ts +4 -0
  262. package/src/organisms/fab-menu/fab-menu.types.ts +2 -0
  263. package/src/organisms/form/form.android.tsx +15 -7
  264. package/src/organisms/form/form.types.ts +6 -0
  265. package/src/organisms/menu/menu.types.ts +17 -0
  266. package/src/organisms/sheet/sheet.types.ts +19 -0
  267. package/src/organisms/snackbar/snackbar.types.ts +8 -0
  268. package/src/organisms/swipe-actions/swipe-actions.android.tsx +129 -9
  269. package/src/organisms/swipe-actions/swipe-actions.ios.tsx +45 -11
  270. package/src/organisms/swipe-actions/swipe-actions.types.ts +37 -14
  271. package/src/ripple-clip.ts +10 -1
  272. package/src/templates/screen/screen.android.tsx +33 -12
  273. package/src/templates/screen/screen.types.ts +9 -0
  274. package/lib/module/atoms/activity-indicator/activity-indicator.js +0 -14
  275. package/lib/module/atoms/activity-indicator/activity-indicator.js.map +0 -1
  276. package/lib/module/atoms/flash-list/flash-list.js +0 -31
  277. package/lib/module/atoms/flash-list/flash-list.js.map +0 -1
  278. package/lib/module/atoms/flash-list/flash-list.types.js +0 -4
  279. package/lib/module/atoms/flash-list/flash-list.types.js.map +0 -1
  280. package/lib/module/atoms/flash-list/index.js +0 -5
  281. package/lib/module/atoms/flash-list/index.js.map +0 -1
  282. package/lib/module/atoms/segmented-control/segmented-control.js +0 -40
  283. package/lib/module/atoms/segmented-control/segmented-control.js.map +0 -1
  284. package/lib/typescript/atoms/flash-list/flash-list.d.ts +0 -8
  285. package/lib/typescript/atoms/flash-list/flash-list.types.d.ts +0 -18
  286. package/lib/typescript/atoms/flash-list/index.d.ts +0 -2
  287. package/src/atoms/activity-indicator/activity-indicator.tsx +0 -9
  288. package/src/atoms/flash-list/flash-list.tsx +0 -28
  289. package/src/atoms/flash-list/flash-list.types.ts +0 -20
  290. package/src/atoms/flash-list/index.ts +0 -2
  291. package/src/atoms/segmented-control/segmented-control.tsx +0 -47
@@ -6,9 +6,13 @@ import {useMemo} from 'react'
6
6
  import {useHostGuard} from '../../host-context'
7
7
  import type {CheckboxComponent, CheckboxProps} from './checkbox.types'
8
8
 
9
+ /** Stands in for a missing handler: Material drops its 48dp touch target without one, and the box moves. */
10
+ const NOOP = () => undefined
11
+
9
12
  /**
10
- * Material's checkbox, tinted with the accent. With a label it is a full-width row that toggles as a whole; the gap is
11
- * explicit because a checkbox without a handler drops Material's 48dp touch target, and with it the space around it.
13
+ * Material's checkbox, tinted with the accent. With a label it is a full-width row that toggles as a whole. It always
14
+ * gets a handler, so a disabled or read-only box keeps the touch target an enabled one has, and lines up with it;
15
+ * `enabled` is what stops the input.
12
16
  */
13
17
  export const Checkbox: CheckboxComponent = ({disabled = false, label, onValueChange, value}: CheckboxProps) => {
14
18
  useHostGuard('Checkbox')
@@ -21,7 +25,7 @@ export const Checkbox: CheckboxComponent = ({disabled = false, label, onValueCha
21
25
  <ComposeCheckbox
22
26
  colors={{checkedColor: theme.color('fill.accent.primary'), checkmarkColor: theme.color('content.on.bg')}}
23
27
  enabled={!disabled}
24
- onCheckedChange={onValueChange}
28
+ onCheckedChange={onValueChange ?? NOOP}
25
29
  value={value}
26
30
  />
27
31
  )
@@ -1,9 +1,14 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /** Material's `Checkbox` on Android. iOS has none, so there it is a SwiftUI `Toggle` — the same switch as `Switch`. */
3
4
  export type CheckboxProps = {
5
+ /** Dims the control and ignores taps. */
4
6
  disabled?: boolean
7
+ /** Text beside the control. On Android the whole labelled row toggles it. */
5
8
  label?: string
9
+ /** Called with the new value when the user toggles it. */
6
10
  onValueChange?: (value: boolean) => void
11
+ /** Whether it is checked. Controlled — update it from `onValueChange`. */
7
12
  value: boolean
8
13
  }
9
14
 
@@ -1,9 +1,14 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /** A filter pill on a `Pressable`, not a native control — the React Native tier, so it needs no host. */
3
4
  export type ChipProps = {
5
+ /** The text in the pill. */
4
6
  label: string
7
+ /** Called when the chip is pressed, with the `selection` haptic. */
5
8
  onPress?: () => void
9
+ /** Draws the chip in the accent colours. Off by default. */
6
10
  selected?: boolean
11
+ /** Test identifier, forwarded to the view. */
7
12
  testID?: string
8
13
  }
9
14
 
@@ -5,9 +5,13 @@ import type {ReactNode} from 'react'
5
5
  * renders nothing and says so in dev. Branch on `Platform.OS` if the screen needs a fallback.
6
6
  */
7
7
  export type ColorPickerProps = {
8
+ /** Text beside the colour well. */
8
9
  label?: string
10
+ /** Called with the colour the user picks. */
9
11
  onValueChange?: (color: string) => void
12
+ /** Lets the user pick an opacity as well. Off by default. */
10
13
  supportsOpacity?: boolean
14
+ /** Test identifier, forwarded to the native view. */
11
15
  testID?: string
12
16
  /** `#RRGGBB` or `#RRGGBBAA`. */
13
17
  value: string | null
@@ -1,9 +1,22 @@
1
- import {DateTimePicker} from '@expo/ui/community/datetime-picker'
2
- import {useCallback} from 'react'
1
+ import {Host, DatePicker as SwiftUIDatePicker} from '@expo/ui/swift-ui'
2
+ import {datePickerStyle, labelsHidden, tint} from '@expo/ui/swift-ui/modifiers'
3
+ import {useAppTheme} from '@hzblj/kara-theme'
4
+ import {useMemo} from 'react'
3
5
 
4
- import type {DatePickerComponent, DatePickerProps} from './date-picker.types'
6
+ import type {DatePickerComponent, DatePickerMode, DatePickerProps} from './date-picker.types'
5
7
 
6
- /** The system's compact date picker, inline: a tap on it opens the calendar popover UIKit owns. */
8
+ const COMPONENTS = {
9
+ date: ['date'],
10
+ datetime: ['date', 'hourAndMinute'],
11
+ time: ['hourAndMinute'],
12
+ } as const satisfies Record<DatePickerMode, ('date' | 'hourAndMinute')[]>
13
+
14
+ /**
15
+ * The system's compact date picker: the value in a pill, and a tap on it opens the calendar popover UIKit owns. Its own
16
+ * host, sized to the pill on both axes — a host that took the width it was given would lay the pill out against the
17
+ * far edge of it, or past its container's — so it drops into a row the way a `Text` does. The label is the caller's,
18
+ * beside it; the calendar is in the accent.
19
+ */
7
20
  export const DatePicker: DatePickerComponent = ({
8
21
  max,
9
22
  min,
@@ -12,16 +25,23 @@ export const DatePicker: DatePickerComponent = ({
12
25
  testID,
13
26
  value,
14
27
  }: DatePickerProps) => {
15
- const onChange = useCallback((_event: unknown, date: Date) => onValueChange?.(date), [onValueChange])
28
+ const theme = useAppTheme()
29
+
30
+ const modifiers = useMemo(
31
+ () => [datePickerStyle('compact'), labelsHidden(), tint(theme.color('fill.accent.primary'))],
32
+ [theme]
33
+ )
16
34
 
17
35
  return (
18
- <DateTimePicker
19
- maximumDate={max}
20
- minimumDate={min}
21
- mode={mode}
22
- onValueChange={onChange}
23
- testID={testID}
24
- value={value}
25
- />
36
+ <Host colorScheme={theme.dark ? 'dark' : 'light'} matchContents>
37
+ <SwiftUIDatePicker
38
+ displayedComponents={[...COMPONENTS[mode]]}
39
+ modifiers={modifiers}
40
+ onDateChange={onValueChange}
41
+ range={min || max ? {end: max, start: min} : undefined}
42
+ selection={value}
43
+ testID={testID}
44
+ />
45
+ </Host>
26
46
  )
27
47
  }
@@ -2,12 +2,26 @@ import type {ReactNode} from 'react'
2
2
 
3
3
  export type DatePickerMode = 'date' | 'datetime' | 'time'
4
4
 
5
+ /**
6
+ * A date or time picker — SwiftUI's compact `DatePicker` on iOS, a field that opens Material's picker dialog on a tap
7
+ * on Android. The React Native tier: it brings its own host and sizes itself to the value, so it sits in a row beside
8
+ * its label like a `Text`.
9
+ */
5
10
  export type DatePickerProps = {
11
+ /** The latest date that can be picked. Android ignores it in `time` mode. */
6
12
  max?: Date
13
+ /** The earliest date that can be picked. Android ignores it in `time` mode. */
7
14
  min?: Date
15
+ /**
16
+ * What it picks. `date` by default. Android has no combined dialog, so there `datetime` picks the date and keeps
17
+ * `value`'s time of day.
18
+ */
8
19
  mode?: DatePickerMode
20
+ /** Called with the new date — on iOS as it changes, on Android when the dialog is confirmed. */
9
21
  onValueChange?: (date: Date) => void
22
+ /** Test identifier — on the native picker on iOS, on the tappable field on Android. */
10
23
  testID?: string
24
+ /** The selected date. Controlled — update it from `onValueChange`. */
11
25
  value: Date
12
26
  }
13
27
 
@@ -1,8 +1,11 @@
1
1
  import type {ColorToken} from '@hzblj/kara-theme'
2
2
  import type {ReactNode} from 'react'
3
3
 
4
+ /** A horizontal hairline — SwiftUI `Divider` / Compose `HorizontalDivider`. */
4
5
  export type DividerProps = {
6
+ /** The line's colour. `border.tertiary` by default. */
5
7
  color?: ColorToken
8
+ /** Test identifier, forwarded to the native view on iOS — Android ignores it. */
6
9
  testID?: string
7
10
  }
8
11
 
@@ -2,9 +2,16 @@ import type {ReactNode} from 'react'
2
2
 
3
3
  import type {IconName} from '../icon'
4
4
 
5
+ /**
6
+ * A floating action button — Material's `FloatingActionButton` on Android, a 56pt accent circle (a plain SwiftUI
7
+ * `Button`) on iOS. It renders inline; pinning it to a corner is up to the screen.
8
+ */
5
9
  export type FabProps = {
10
+ /** The icon, one of the app's, drawn in `content.on.bg`. */
6
11
  name: IconName
12
+ /** Called when the button is pressed. */
7
13
  onPress?: () => void
14
+ /** Test identifier, forwarded to the native view on iOS — Android ignores it. */
8
15
  testID?: string
9
16
  }
10
17
 
@@ -1,5 +1,43 @@
1
- import {GlassFallback} from './glass-fallback'
1
+ import {BackdropBlurView} from '@hzblj/kara-native/jetpack-compose'
2
+ import {useSx} from '@hzblj/kara-sx'
3
+ import {radii, useAppTheme, withOpacity} from '@hzblj/kara-theme'
4
+
2
5
  import type {GlassViewComponent, GlassViewProps} from './glass-view.types'
3
6
 
4
- /** Android has no Liquid Glass, so this is always the fallback capsule. */
5
- export const GlassView: GlassViewComponent = (props: GlassViewProps) => <GlassFallback {...props} />
7
+ /** dp. Strong enough that a photo behind reads as colour and light, not as detail. */
8
+ const BLUR_RADIUS = 16
9
+
10
+ /**
11
+ * Frosted glass, Android's way: what lies behind the capsule, blurred (Android 12+), under a tonal Material surface
12
+ * and a light hairline — the blurred surfaces Android 16 gives its own system UI, not a copy of iOS's lens. Without a
13
+ * tint it is the surface colour, translucent, so the content keeps its usual colours on it; a tint fills it with that
14
+ * colour instead. Below Android 12, with nothing to blur, the same colours are drawn near-opaque. Android has no swell,
15
+ * so `interactive` changes nothing here — the `Pressable` around it gives the press its feedback.
16
+ */
17
+ export const GlassView: GlassViewComponent = ({
18
+ children,
19
+ interactive: _interactive,
20
+ testID,
21
+ tint,
22
+ ...sx
23
+ }: GlassViewProps) => {
24
+ const theme = useAppTheme()
25
+ const style = useSx(sx)
26
+ const base = String(theme.color(tint ?? 'surface.primary'))
27
+
28
+ return (
29
+ <BackdropBlurView
30
+ blurRadius={BLUR_RADIUS}
31
+ cornerRadius={radii.full}
32
+ fallbackTint={withOpacity(base, 'o95')}
33
+ glassTint={withOpacity(base, tint ? 'o85' : 'o50')}
34
+ hairlineColor={withOpacity(String(theme.color('content.on.bg')), theme.dark ? 'o20' : 'o60')}
35
+ hairlineWidth={1}
36
+ saturation={1.6}
37
+ style={style}
38
+ testID={testID}
39
+ >
40
+ {children}
41
+ </BackdropBlurView>
42
+ )
43
+ }
@@ -3,17 +3,25 @@ import type {ColorToken} from '@hzblj/kara-theme'
3
3
  import type {ReactNode} from 'react'
4
4
 
5
5
  /**
6
- * iOS 26 Liquid Glass around a React Native subtree, shaped as a capsule — the React Native tier, so it needs no host
7
- * and costs next to nothing in a list cell. Where there is no Liquid Glass (Android, and iOS before 26) it draws a
8
- * capsule of `tint` instead, or of the glass buttons' translucent fill (`btn.bg.glass.base`) when there is none.
6
+ * Glass around a React Native subtree, shaped as a capsule — the React Native tier, so it needs no host. iOS 26:
7
+ * Liquid Glass. Android: frosted glass — what lies behind, blurred on Android 12+, under a translucent tonal surface
8
+ * (or `tint`) and a hairline. iOS before 26 has no Liquid Glass and draws a capsule of `tint` instead, or of the glass
9
+ * buttons' translucent fill (`btn.bg.glass.base`) when there is none.
10
+ *
11
+ * On Android each glass re-records what lies behind it before every frame: a few buttons cost nothing, but keep it out
12
+ * of the cells of a long list — a plain `tint` reads as well there.
9
13
  */
10
14
  export type GlassViewProps = ResponsiveViewSx & {
11
15
  /** React Native views. Give them `pointerEvents="none"` when `interactive`, so the press reaches the `Pressable`. */
12
16
  children?: ReactNode
13
- /** Swells toward the finger on touch, carrying the children with it. Wrap the view in a `Pressable` for the press. */
17
+ /**
18
+ * iOS 26: swells toward the finger on touch, carrying the children with it. Android has no swell: wrap the view in a
19
+ * `Pressable` with `borderRadius="full"`, whose ripple then keeps to the capsule. The press is always the `Pressable`'s.
20
+ */
14
21
  interactive?: boolean
22
+ /** Test identifier, forwarded to the view. */
15
23
  testID?: string
16
- /** Tints the material. Unset leaves it clear. */
24
+ /** Tints the material. Unset leaves it clear on iOS, and the translucent surface colour on Android. */
17
25
  tint?: ColorToken
18
26
  }
19
27
 
@@ -4,11 +4,17 @@ import type {ReactNode} from 'react'
4
4
 
5
5
  export type HStackAlign = 'center' | 'end' | 'start'
6
6
 
7
+ /** A horizontal stack — SwiftUI `HStack` / Compose `Row`. */
7
8
  export type HStackProps = ResponsiveSx & {
9
+ /** How the children line up vertically — `start` is the top, `end` the bottom. `start` by default. */
8
10
  align?: HStackAlign
11
+ /** Native views, laid out from the leading edge. */
9
12
  children?: ReactNode
13
+ /** Makes the stack pressable — a tap gesture on iOS, a `clickable` with Material's ripple on Android. */
10
14
  onPress?: () => void
15
+ /** The gap between children. Unset, SwiftUI puts its default spacing between them and Compose none. */
11
16
  spacing?: SpaceSize
17
+ /** Test identifier, forwarded to the native view. */
12
18
  testID?: string
13
19
  }
14
20
 
@@ -18,12 +18,20 @@ type DeclaredIconName = keyof KaraIcons & string
18
18
  */
19
19
  export type IconName = [DeclaredIconName] extends [never] ? string : DeclaredIconName
20
20
 
21
+ /**
22
+ * One of the app's icons, in a host — SwiftUI `Image` from the asset catalog on iOS, Material's `Icon` with the vector
23
+ * drawable on Android. `RnIcon` draws the same asset outside one.
24
+ */
21
25
  export type IconProps = {
22
26
  /** What a screen reader says. Unset, the icon is decorative and hidden from it. */
23
27
  accessibilityLabel?: string
28
+ /** The icon's tint. `content.primary` by default. */
24
29
  color?: ColorToken
30
+ /** Which of the app's icons — a key of `KaraIcons`. */
25
31
  name: IconName
32
+ /** Its width and height, a spacing token. `s24` by default. */
26
33
  size?: SpaceSize
34
+ /** Test identifier, forwarded to the native view on iOS — Android ignores it. */
27
35
  testID?: string
28
36
  }
29
37
 
@@ -2,10 +2,15 @@ import type {ReactNode} from 'react'
2
2
 
3
3
  import type {IconName} from '../icon'
4
4
 
5
+ /** A button that is just an icon — a plain SwiftUI `Button` on iOS, Material's `IconButton` on Android. */
5
6
  export type IconButtonProps = {
7
+ /** Draws the icon in `btn.content.disabled` and stops the button responding to presses. */
6
8
  disabled?: boolean
9
+ /** The icon, one of the app's. */
7
10
  name: IconName
11
+ /** Called when the button is pressed. */
8
12
  onPress?: () => void
13
+ /** Test identifier, forwarded to the native view on iOS — Android ignores it. */
9
14
  testID?: string
10
15
  }
11
16
 
@@ -1,15 +1,14 @@
1
+ import {sx} from '@hzblj/kara-sx'
1
2
  import {duration} from '@hzblj/kara-theme'
2
- import {Image as ExpoImage, ImageProps} from 'expo-image'
3
- import {FC} from 'react'
3
+ import {Image as ExpoImage, type ImageProps as ExpoImageProps} from 'expo-image'
4
+ import type {Ref} from 'react'
4
5
 
5
- const Image: FC<ImageProps> = props => (
6
- <ExpoImage
7
- transition={{
8
- duration: duration.d100,
9
- timing: 'linear',
10
- }}
11
- {...props}
12
- />
13
- )
6
+ const FADE = {duration: duration.d100, timing: 'linear'} as const
14
7
 
15
- export {Image}
8
+ const FadingImage = (props: ExpoImageProps & {ref?: Ref<ExpoImage>}) => <ExpoImage transition={FADE} {...props} />
9
+
10
+ /**
11
+ * `expo-image` with the `sx` props, the way `Box` is a `View` with them, and the design system's 100 ms linear fade
12
+ * already applied, so a loaded image never pops in. An explicit `transition` wins, and so does an explicit `style`.
13
+ */
14
+ export const Image = sx(FadingImage)
@@ -0,0 +1,6 @@
1
+ import type {ComponentProps} from 'react'
2
+
3
+ import type {Image} from './image'
4
+
5
+ /** Derived from the component itself, so it cannot drift from what `sx(Image)` actually accepts. */
6
+ export type ImageProps = ComponentProps<typeof Image>
@@ -1 +1,2 @@
1
1
  export * from './image'
2
+ export * from './image.types'
@@ -1,6 +1,7 @@
1
1
  export * from './activity-indicator'
2
2
  export * from './avatar'
3
3
  export * from './badge'
4
+ export * from './blur-view'
4
5
  export * from './box'
5
6
  export * from './button'
6
7
  export * from './card'
@@ -10,7 +11,6 @@ export * from './color-picker'
10
11
  export * from './date-picker'
11
12
  export * from './divider'
12
13
  export * from './fab'
13
- export * from './flash-list'
14
14
  export * from './glass-view'
15
15
  export * from './h-stack'
16
16
  export * from './icon'
@@ -1,4 +1,5 @@
1
1
  import {Host} from '@expo/ui/jetpack-compose'
2
+ import {useSx} from '@hzblj/kara-sx'
2
3
  import {useAppTheme} from '@hzblj/kara-theme'
3
4
 
4
5
  import {HostBoundary} from '../../host-context'
@@ -8,15 +9,22 @@ const MATCH_HEIGHT_ONLY = {horizontal: false, vertical: true}
8
9
 
9
10
  const MATCH_NEITHER = {horizontal: false, vertical: false}
10
11
 
11
- export const Island: IslandComponent = ({children, matchContents = true, style}: IslandProps) => {
12
+ export const Island: IslandComponent = ({
13
+ children,
14
+ matchContents = true,
15
+ style,
16
+ testID: _testID,
17
+ ...sx
18
+ }: IslandProps) => {
12
19
  const theme = useAppTheme()
20
+ const resolved = useSx(sx)
13
21
 
14
22
  return (
15
23
  <Host
16
24
  colorScheme={theme.dark ? 'dark' : 'light'}
17
25
  matchContents={matchContents ? MATCH_HEIGHT_ONLY : MATCH_NEITHER}
18
26
  seedColor={theme.materialSeed}
19
- style={style}
27
+ style={style ? [resolved, style] : resolved}
20
28
  >
21
29
  <HostBoundary>{children}</HostBoundary>
22
30
  </Host>
@@ -1,5 +1,6 @@
1
1
  import {Host} from '@expo/ui/swift-ui'
2
2
  import {dynamicTypeSize} from '@expo/ui/swift-ui/modifiers'
3
+ import {useSx} from '@hzblj/kara-sx'
3
4
  import {MAX_DYNAMIC_TYPE_SIZE, useAppTheme} from '@hzblj/kara-theme'
4
5
 
5
6
  import {HostBoundary} from '../../host-context'
@@ -11,8 +12,9 @@ const MATCH_HEIGHT_ONLY = {horizontal: false, vertical: true}
11
12
 
12
13
  const MATCH_NEITHER = {horizontal: false, vertical: false}
13
14
 
14
- export const Island: IslandComponent = ({children, matchContents = true, style, testID}: IslandProps) => {
15
+ export const Island: IslandComponent = ({children, matchContents = true, style, testID, ...sx}: IslandProps) => {
15
16
  const theme = useAppTheme()
17
+ const resolved = useSx(sx)
16
18
 
17
19
  return (
18
20
  <Host
@@ -20,7 +22,7 @@ export const Island: IslandComponent = ({children, matchContents = true, style,
20
22
  matchContents={matchContents ? MATCH_HEIGHT_ONLY : MATCH_NEITHER}
21
23
  modifiers={DYNAMIC_TYPE_CAP}
22
24
  seedColor={theme.materialSeed}
23
- style={style}
25
+ style={style ? [resolved, style] : resolved}
24
26
  testID={testID}
25
27
  >
26
28
  <HostBoundary>{children}</HostBoundary>
@@ -1,7 +1,13 @@
1
+ import type {ResponsiveViewSx} from '@hzblj/kara-sx'
1
2
  import type {ReactNode} from 'react'
2
3
  import type {StyleProp, ViewStyle} from 'react-native'
3
4
 
4
- export type IslandProps = {
5
+ /**
6
+ * A host for native atoms in a React Native layout — the SwiftUI / Compose `Host` an `@expo/ui` tree mounts into. It
7
+ * sits in that layout like a `Box`, and takes `Box`'s `sx` props for it: `h`, `flex`, margins.
8
+ */
9
+ export type IslandProps = ResponsiveViewSx & {
10
+ /** The native views it hosts. */
5
11
  children?: ReactNode
6
12
  /**
7
13
  * Sizes the island's height to its native content while the width still comes from the parent, which
@@ -9,13 +15,15 @@ export type IslandProps = {
9
15
  * width maps to `fixedSize(horizontal: true)`, so the native side ignores the proposed width and
10
16
  * resolves a `wide` stack's ideal width as unbounded.
11
17
  *
12
- * Turn it off — and give the island an explicit size via `style` — when the subtree contains an
18
+ * Turn it off — and give the island an explicit height, `h` — when the subtree contains an
13
19
  * `RnView`: that host drives its SwiftUI frame from the child's Yoga-laid-out bounds while this one
14
20
  * writes its measured size back into the Yoga style, and the two together spin a layout loop that
15
21
  * hangs the main thread as soon as the views lay out on screen.
16
22
  */
17
23
  matchContents?: boolean
24
+ /** A React Native style for the host view, merged over the `sx` props — for what they cannot say. */
18
25
  style?: StyleProp<ViewStyle>
26
+ /** Test identifier, forwarded to the host view on iOS — Android ignores it. */
19
27
  testID?: string
20
28
  }
21
29
 
@@ -10,12 +10,14 @@ import type {ReactNode} from 'react'
10
10
  * the children is one too many: on iOS it becomes an empty row of its own.
11
11
  */
12
12
  export type ListProps = {
13
+ /** The rows — native views, one row each. */
13
14
  children: ReactNode
14
15
  /**
15
16
  * Enables pull-to-refresh. Async on purpose — see `ScrollViewProps.onRefresh`: the spinner runs
16
17
  * until the promise settles, so neither platform needs a `refreshing` flag kept in sync by hand.
17
18
  */
18
19
  onRefresh?: () => Promise<void>
20
+ /** Test identifier, forwarded to the native view on iOS — Android ignores it. */
19
21
  testID?: string
20
22
  }
21
23
 
@@ -1,15 +1,27 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /** One choice of a `Picker`. */
3
4
  export type PickerOption = {
5
+ /** What the user reads. */
4
6
  label: string
7
+ /** What `onValueChange` reports. It also keys the option, so it must be unique. */
5
8
  value: string
6
9
  }
7
10
 
11
+ /**
12
+ * A single choice from a list — a SwiftUI wheel picker on iOS, Material's exposed dropdown on Android. It brings its
13
+ * own host, so it renders outside `Screen` and `Island` as well.
14
+ */
8
15
  export type PickerProps = {
16
+ /** Stops the user changing the selection, and dims the picker. */
9
17
  disabled?: boolean
18
+ /** Called with the chosen option's `value`. */
10
19
  onValueChange?: (value: string) => void
20
+ /** The choices, in order. */
11
21
  options: PickerOption[]
22
+ /** Android: the dropdown field's test tag. iOS: `@expo/ui`'s wheel picker drops it. */
12
23
  testID?: string
24
+ /** The selected option's `value`. Controlled: pass back what `onValueChange` reports. */
13
25
  value: string
14
26
  }
15
27
 
@@ -1,9 +1,11 @@
1
1
  import {useSplitSx} from '@hzblj/kara-sx'
2
- import {useCallback} from 'react'
3
- import {Pressable as RNPressable} from 'react-native'
2
+ import {useAppTheme, withOpacity} from '@hzblj/kara-theme'
3
+ import {useCallback, useMemo} from 'react'
4
+ import {Platform, Pressable as RNPressable, View, type ViewStyle} from 'react-native'
4
5
  import Animated, {useAnimatedStyle, useSharedValue, withSpring} from 'react-native-reanimated'
5
6
 
6
7
  import {haptics} from '../../haptics'
8
+ import {splitForRipple} from '../../ripple-clip'
7
9
  import type {PressableComponent, PressableProps} from './pressable.types'
8
10
 
9
11
  const SPRING = {damping: 20, stiffness: 400}
@@ -22,20 +24,39 @@ export const Pressable: PressableComponent = ({
22
24
  const scale = useSharedValue(1)
23
25
  const feedback = haptic === true ? 'light' : haptic || undefined
24
26
 
27
+ const theme = useAppTheme()
28
+ const isAndroid = Platform.OS === 'android'
29
+
25
30
  const rStyle = useAnimatedStyle(() => ({transform: [{scale: scale.value}]}), [])
26
31
 
32
+ // Android's press is Material's: a ripple over the content — the pressed state layer, `onSurface` at 10 % — clipped to
33
+ // the shape by an outer view (`splitForRipple`), and nothing moves. iOS springs down.
34
+ const ripple = useMemo(
35
+ () =>
36
+ isAndroid ? {color: withOpacity(String(theme.color('content.primary')), 'o10'), foreground: true} : undefined,
37
+ [isAndroid, theme]
38
+ )
39
+ const androidStyle = useMemo(
40
+ () => (isAndroid ? splitForRipple(resolved as ViewStyle) : undefined),
41
+ [isAndroid, resolved]
42
+ )
43
+
27
44
  // The finger is down: the Taptic Engine has until the release to wake, so the haptic lands with the press.
28
45
  const onPressIn = useCallback(() => {
29
- scale.value = withSpring(0.96, SPRING)
46
+ if (!isAndroid) {
47
+ scale.value = withSpring(0.96, SPRING)
48
+ }
30
49
 
31
50
  if (feedback) {
32
51
  haptics.prepare(feedback)
33
52
  }
34
- }, [feedback, scale])
53
+ }, [feedback, isAndroid, scale])
35
54
 
36
55
  const onPressOut = useCallback(() => {
37
- scale.value = withSpring(1, SPRING)
38
- }, [scale])
56
+ if (!isAndroid) {
57
+ scale.value = withSpring(1, SPRING)
58
+ }
59
+ }, [isAndroid, scale])
39
60
 
40
61
  const onPressWithHaptic = useCallback(() => {
41
62
  if (feedback) {
@@ -45,6 +66,25 @@ export const Pressable: PressableComponent = ({
45
66
  onPress?.()
46
67
  }, [feedback, onPress])
47
68
 
69
+ if (androidStyle) {
70
+ return (
71
+ <View style={androidStyle.outer}>
72
+ <RNPressable
73
+ android_ripple={ripple}
74
+ disabled={disabled}
75
+ hitSlop={8}
76
+ onPress={onPressWithHaptic}
77
+ onPressIn={onPressIn}
78
+ style={androidStyle.inner}
79
+ testID={testID}
80
+ {...rest}
81
+ >
82
+ {children}
83
+ </RNPressable>
84
+ </View>
85
+ )
86
+ }
87
+
48
88
  return (
49
89
  <AnimatedPressable
50
90
  disabled={disabled}
@@ -3,12 +3,22 @@ import type {ReactNode} from 'react'
3
3
 
4
4
  import type {Haptic} from '../../haptics'
5
5
 
6
+ /**
7
+ * React Native's `Pressable`, styled through `sx` like a `Box`, taking touches 8 points past its edges. The press is
8
+ * each platform's own: iOS springs it down to 96%; Android draws Material's ripple over the content, clipped to the
9
+ * pressable's shape — give it the `borderRadius` of what it wraps (`full` around a `GlassView`). The React Native
10
+ * tier — it needs no host.
11
+ */
6
12
  export type PressableProps = ResponsiveViewSx & {
13
+ /** The pressable content. */
7
14
  children?: ReactNode
15
+ /** Ignores presses — no `onPress`, no haptic, no spring or ripple. */
8
16
  disabled?: boolean
9
17
  /** Played on press. `true` is a `light` impact; a name picks the haptic, e.g. `selection` for a choice. */
10
18
  haptic?: boolean | Haptic
19
+ /** Called when the press is released, after the `haptic` plays. */
11
20
  onPress?: () => void
21
+ /** Test identifier, forwarded to the view. */
12
22
  testID?: string
13
23
  }
14
24
 
@@ -1,7 +1,13 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /**
4
+ * A progress bar or ring, tinted `fill.accent.primary` — SwiftUI `ProgressView` / Compose's
5
+ * `LinearProgressIndicator` and `CircularProgressIndicator`.
6
+ */
3
7
  export type ProgressProps = {
8
+ /** A circular indicator instead of a bar. Off by default. */
4
9
  circular?: boolean
10
+ /** Between `0` and `1`. Leave it unset for an indeterminate indicator. */
5
11
  value?: number
6
12
  }
7
13