@hzblj/kara-components 0.20.1 → 0.22.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 (488) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/lib/module/atoms/activity-indicator/activity-indicator.android.js +1 -1
  3. package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
  4. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +1 -1
  5. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
  6. package/lib/module/atoms/badge/badge.android.js +1 -1
  7. package/lib/module/atoms/badge/badge.android.js.map +1 -1
  8. package/lib/module/atoms/badge/badge.ios.js +1 -1
  9. package/lib/module/atoms/badge/badge.ios.js.map +1 -1
  10. package/lib/module/atoms/blur-view/blur-view.android.js +22 -1
  11. package/lib/module/atoms/blur-view/blur-view.android.js.map +1 -1
  12. package/lib/module/atoms/blur-view/blur-view.ios.js +20 -0
  13. package/lib/module/atoms/blur-view/blur-view.ios.js.map +1 -1
  14. package/lib/module/atoms/button/button.android.js +1 -1
  15. package/lib/module/atoms/button/button.android.js.map +1 -1
  16. package/lib/module/atoms/button/button.ios.js +1 -1
  17. package/lib/module/atoms/button/button.ios.js.map +1 -1
  18. package/lib/module/atoms/card/card.android.js +1 -1
  19. package/lib/module/atoms/card/card.android.js.map +1 -1
  20. package/lib/module/atoms/card/card.ios.js +1 -1
  21. package/lib/module/atoms/card/card.ios.js.map +1 -1
  22. package/lib/module/atoms/checkbox/checkbox.android.js +1 -1
  23. package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
  24. package/lib/module/atoms/checkbox/checkbox.ios.js +1 -1
  25. package/lib/module/atoms/checkbox/checkbox.ios.js.map +1 -1
  26. package/lib/module/atoms/color-picker/color-picker.android.js +1 -1
  27. package/lib/module/atoms/color-picker/color-picker.android.js.map +1 -1
  28. package/lib/module/atoms/color-picker/color-picker.ios.js +1 -1
  29. package/lib/module/atoms/color-picker/color-picker.ios.js.map +1 -1
  30. package/lib/module/atoms/date-picker/date-picker.android.js +1 -1
  31. package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -1
  32. package/lib/module/atoms/date-picker/date-picker.ios.js +1 -1
  33. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
  34. package/lib/module/atoms/divider/divider.android.js +1 -1
  35. package/lib/module/atoms/divider/divider.android.js.map +1 -1
  36. package/lib/module/atoms/divider/divider.ios.js +1 -1
  37. package/lib/module/atoms/divider/divider.ios.js.map +1 -1
  38. package/lib/module/atoms/fab/fab.android.js +1 -1
  39. package/lib/module/atoms/fab/fab.android.js.map +1 -1
  40. package/lib/module/atoms/fab/fab.ios.js +1 -1
  41. package/lib/module/atoms/fab/fab.ios.js.map +1 -1
  42. package/lib/module/atoms/glyph/glyph-button.android.js.map +1 -0
  43. package/lib/module/atoms/glyph/glyph-button.ios.js.map +1 -0
  44. package/lib/module/atoms/glyph/glyph-button.types.js.map +1 -0
  45. package/lib/module/atoms/glyph/glyph.android.js.map +1 -0
  46. package/lib/module/atoms/glyph/glyph.ios.js.map +1 -0
  47. package/lib/module/atoms/glyph/glyph.types.js.map +1 -0
  48. package/lib/module/atoms/glyph/index.js.map +1 -0
  49. package/lib/module/atoms/glyph/rn-glyph.android.js.map +1 -0
  50. package/lib/module/atoms/glyph/rn-glyph.ios.js.map +1 -0
  51. package/lib/module/atoms/h-stack/h-stack.android.js +2 -2
  52. package/lib/module/atoms/h-stack/h-stack.android.js.map +1 -1
  53. package/lib/module/atoms/h-stack/h-stack.ios.js +2 -2
  54. package/lib/module/atoms/h-stack/h-stack.ios.js.map +1 -1
  55. package/lib/module/atoms/icon/icon.android.js +1 -1
  56. package/lib/module/atoms/icon/icon.android.js.map +1 -1
  57. package/lib/module/atoms/icon/icon.ios.js +1 -1
  58. package/lib/module/atoms/icon/icon.ios.js.map +1 -1
  59. package/lib/module/atoms/icon-button/icon-button.android.js +1 -1
  60. package/lib/module/atoms/icon-button/icon-button.android.js.map +1 -1
  61. package/lib/module/atoms/icon-button/icon-button.ios.js +1 -1
  62. package/lib/module/atoms/icon-button/icon-button.ios.js.map +1 -1
  63. package/lib/module/atoms/island/island.android.js +3 -1
  64. package/lib/module/atoms/island/island.android.js.map +1 -1
  65. package/lib/module/atoms/island/island.ios.js +3 -1
  66. package/lib/module/atoms/island/island.ios.js.map +1 -1
  67. package/lib/module/atoms/list/list.android.js +2 -2
  68. package/lib/module/atoms/list/list.android.js.map +1 -1
  69. package/lib/module/atoms/list/list.ios.js +1 -1
  70. package/lib/module/atoms/list/list.ios.js.map +1 -1
  71. package/lib/module/atoms/picker/picker.android.js +2 -2
  72. package/lib/module/atoms/picker/picker.android.js.map +1 -1
  73. package/lib/module/atoms/picker/picker.ios.js +1 -1
  74. package/lib/module/atoms/picker/picker.ios.js.map +1 -1
  75. package/lib/module/atoms/pressable/pressable.js +1 -1
  76. package/lib/module/atoms/pressable/pressable.js.map +1 -1
  77. package/lib/module/atoms/progress/progress.android.js +1 -1
  78. package/lib/module/atoms/progress/progress.android.js.map +1 -1
  79. package/lib/module/atoms/progress/progress.ios.js +1 -1
  80. package/lib/module/atoms/progress/progress.ios.js.map +1 -1
  81. package/lib/module/atoms/scroll-view/scroll-view.android.js +2 -2
  82. package/lib/module/atoms/scroll-view/scroll-view.android.js.map +1 -1
  83. package/lib/module/atoms/scroll-view/scroll-view.ios.js +1 -1
  84. package/lib/module/atoms/scroll-view/scroll-view.ios.js.map +1 -1
  85. package/lib/module/atoms/segmented-control/segmented-control.android.js +1 -1
  86. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
  87. package/lib/module/atoms/segmented-control/segmented-control.ios.js +1 -1
  88. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
  89. package/lib/module/atoms/slider/slider.android.js +1 -1
  90. package/lib/module/atoms/slider/slider.android.js.map +1 -1
  91. package/lib/module/atoms/slider/slider.ios.js +1 -1
  92. package/lib/module/atoms/slider/slider.ios.js.map +1 -1
  93. package/lib/module/atoms/spacer/spacer.android.js +1 -1
  94. package/lib/module/atoms/spacer/spacer.android.js.map +1 -1
  95. package/lib/module/atoms/spacer/spacer.ios.js +1 -1
  96. package/lib/module/atoms/spacer/spacer.ios.js.map +1 -1
  97. package/lib/module/atoms/surface/surface.android.js +1 -1
  98. package/lib/module/atoms/surface/surface.android.js.map +1 -1
  99. package/lib/module/atoms/surface/surface.ios.js +1 -1
  100. package/lib/module/atoms/surface/surface.ios.js.map +1 -1
  101. package/lib/module/atoms/switch/switch.android.js +1 -1
  102. package/lib/module/atoms/switch/switch.android.js.map +1 -1
  103. package/lib/module/atoms/switch/switch.ios.js +1 -1
  104. package/lib/module/atoms/switch/switch.ios.js.map +1 -1
  105. package/lib/module/atoms/text/text.android.js +1 -1
  106. package/lib/module/atoms/text/text.android.js.map +1 -1
  107. package/lib/module/atoms/text/text.ios.js +1 -1
  108. package/lib/module/atoms/text/text.ios.js.map +1 -1
  109. package/lib/module/atoms/text-field/text-field.android.js +2 -2
  110. package/lib/module/atoms/text-field/text-field.android.js.map +1 -1
  111. package/lib/module/atoms/text-field/text-field.ios.js +1 -1
  112. package/lib/module/atoms/text-field/text-field.ios.js.map +1 -1
  113. package/lib/module/atoms/v-stack/v-stack.android.js +2 -2
  114. package/lib/module/atoms/v-stack/v-stack.android.js.map +1 -1
  115. package/lib/module/atoms/v-stack/v-stack.ios.js +2 -2
  116. package/lib/module/atoms/v-stack/v-stack.ios.js.map +1 -1
  117. package/lib/module/atoms/z-stack/z-stack.android.js +2 -2
  118. package/lib/module/atoms/z-stack/z-stack.android.js.map +1 -1
  119. package/lib/module/atoms/z-stack/z-stack.ios.js +2 -2
  120. package/lib/module/atoms/z-stack/z-stack.ios.js.map +1 -1
  121. package/lib/module/hooks/host-context.js.map +1 -0
  122. package/lib/module/hooks/use-refresh-state.js.map +1 -0
  123. package/lib/module/hooks/use-stack-modifiers.js.map +1 -0
  124. package/lib/module/molecules/collapsible/collapsible.android.js +2 -2
  125. package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
  126. package/lib/module/molecules/collapsible/collapsible.ios.js +1 -1
  127. package/lib/module/molecules/collapsible/collapsible.ios.js.map +1 -1
  128. package/lib/module/molecules/labeled-content/labeled-content.js +1 -1
  129. package/lib/module/molecules/labeled-content/labeled-content.js.map +1 -1
  130. package/lib/module/molecules/list-item/list-item.js +1 -1
  131. package/lib/module/molecules/list-item/list-item.js.map +1 -1
  132. package/lib/module/molecules/navigation-bar/navigation-bar.constants.js +5 -0
  133. package/lib/module/molecules/navigation-bar/navigation-bar.constants.js.map +1 -0
  134. package/lib/module/molecules/navigation-bar/navigation-bar.ios.js +31 -4
  135. package/lib/module/molecules/navigation-bar/navigation-bar.ios.js.map +1 -1
  136. package/lib/module/molecules/pager/pager.android.js +1 -1
  137. package/lib/module/molecules/pager/pager.android.js.map +1 -1
  138. package/lib/module/molecules/pager/pager.ios.js +1 -1
  139. package/lib/module/molecules/pager/pager.ios.js.map +1 -1
  140. package/lib/module/molecules/radio-group/radio-group.android.js +1 -1
  141. package/lib/module/molecules/radio-group/radio-group.android.js.map +1 -1
  142. package/lib/module/molecules/radio-group/radio-group.ios.js +2 -2
  143. package/lib/module/molecules/radio-group/radio-group.ios.js.map +1 -1
  144. package/lib/module/molecules/rn-list-item/rn-list-item.js +1 -1
  145. package/lib/module/molecules/rn-list-item/rn-list-item.js.map +1 -1
  146. package/lib/module/molecules/rn-tooltip/rn-tooltip.android.js +1 -1
  147. package/lib/module/molecules/rn-tooltip/rn-tooltip.android.js.map +1 -1
  148. package/lib/module/molecules/search-bar/search-bar.js +1 -1
  149. package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
  150. package/lib/module/molecules/stepper/stepper.android.js +2 -2
  151. package/lib/module/molecules/stepper/stepper.android.js.map +1 -1
  152. package/lib/module/molecules/stepper/stepper.ios.js +1 -1
  153. package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
  154. package/lib/module/molecules/tooltip/tooltip.android.js +1 -1
  155. package/lib/module/molecules/tooltip/tooltip.android.js.map +1 -1
  156. package/lib/module/molecules/tooltip/tooltip.ios.js +1 -1
  157. package/lib/module/molecules/tooltip/tooltip.ios.js.map +1 -1
  158. package/lib/module/organisms/action-sheet/action-sheet.android.js +1 -1
  159. package/lib/module/organisms/action-sheet/action-sheet.android.js.map +1 -1
  160. package/lib/module/organisms/action-sheet/action-sheet.ios.js +1 -1
  161. package/lib/module/organisms/action-sheet/action-sheet.ios.js.map +1 -1
  162. package/lib/module/organisms/alert/alert.android.js +1 -1
  163. package/lib/module/organisms/alert/alert.android.js.map +1 -1
  164. package/lib/module/organisms/alert/alert.ios.js +1 -1
  165. package/lib/module/organisms/alert/alert.ios.js.map +1 -1
  166. package/lib/module/organisms/context-menu/context-menu.android.js +2 -2
  167. package/lib/module/organisms/context-menu/context-menu.android.js.map +1 -1
  168. package/lib/module/organisms/fab-menu/fab-menu.android.js +2 -2
  169. package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
  170. package/lib/module/organisms/fab-menu/fab-menu.ios.js +2 -2
  171. package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
  172. package/lib/module/organisms/form/form.android.js +1 -1
  173. package/lib/module/organisms/form/form.android.js.map +1 -1
  174. package/lib/module/organisms/form/form.ios.js +1 -1
  175. package/lib/module/organisms/form/form.ios.js.map +1 -1
  176. package/lib/module/organisms/header/header-color.js +8 -0
  177. package/lib/module/organisms/header/header-color.js.map +1 -0
  178. package/lib/module/organisms/header/header-inset.android.js +6 -0
  179. package/lib/module/organisms/header/header-inset.android.js.map +1 -0
  180. package/lib/module/organisms/header/header-inset.ios.js +19 -0
  181. package/lib/module/organisms/header/header-inset.ios.js.map +1 -0
  182. package/lib/module/organisms/header/header-large-title-fade.android.js +6 -0
  183. package/lib/module/organisms/header/header-large-title-fade.android.js.map +1 -0
  184. package/lib/module/organisms/header/header-large-title-fade.ios.js +15 -0
  185. package/lib/module/organisms/header/header-large-title-fade.ios.js.map +1 -0
  186. package/lib/module/organisms/header/header-options.android.js +180 -0
  187. package/lib/module/organisms/header/header-options.android.js.map +1 -0
  188. package/lib/module/organisms/header/header-options.ios.js +145 -0
  189. package/lib/module/organisms/header/header-options.ios.js.map +1 -0
  190. package/lib/module/organisms/header/header-options.types.js +4 -0
  191. package/lib/module/organisms/header/header-options.types.js.map +1 -0
  192. package/lib/module/organisms/header/header-search-accessory.android.js +5 -0
  193. package/lib/module/organisms/header/header-search-accessory.android.js.map +1 -0
  194. package/lib/module/organisms/header/header-search-accessory.ios.js +35 -0
  195. package/lib/module/organisms/header/header-search-accessory.ios.js.map +1 -0
  196. package/lib/module/organisms/header/header-search-accessory.types.js +4 -0
  197. package/lib/module/organisms/header/header-search-accessory.types.js.map +1 -0
  198. package/lib/module/organisms/header/header-search-input.android.js +80 -0
  199. package/lib/module/organisms/header/header-search-input.android.js.map +1 -0
  200. package/lib/module/organisms/header/header-search-input.ios.js +8 -0
  201. package/lib/module/organisms/header/header-search-input.ios.js.map +1 -0
  202. package/lib/module/organisms/header/header-search-input.types.js +4 -0
  203. package/lib/module/organisms/header/header-search-input.types.js.map +1 -0
  204. package/lib/module/organisms/header/header-search-scrim.js +62 -0
  205. package/lib/module/organisms/header/header-search-scrim.js.map +1 -0
  206. package/lib/module/organisms/header/header-title.js +230 -0
  207. package/lib/module/organisms/header/header-title.js.map +1 -0
  208. package/lib/module/organisms/header/header.types.js +4 -0
  209. package/lib/module/organisms/header/header.types.js.map +1 -0
  210. package/lib/module/organisms/header/hero-header-background.android.js +33 -0
  211. package/lib/module/organisms/header/hero-header-background.android.js.map +1 -0
  212. package/lib/module/organisms/header/hero-header-background.ios.js +40 -0
  213. package/lib/module/organisms/header/hero-header-background.ios.js.map +1 -0
  214. package/lib/module/organisms/header/hero-header-background.types.js +4 -0
  215. package/lib/module/organisms/header/hero-header-background.types.js.map +1 -0
  216. package/lib/module/organisms/header/index.js +14 -0
  217. package/lib/module/organisms/header/index.js.map +1 -0
  218. package/lib/module/organisms/header/native-subtitle.android.js +5 -0
  219. package/lib/module/organisms/header/native-subtitle.android.js.map +1 -0
  220. package/lib/module/organisms/header/native-subtitle.ios.js +22 -0
  221. package/lib/module/organisms/header/native-subtitle.ios.js.map +1 -0
  222. package/lib/module/organisms/header/native-subtitle.types.js +4 -0
  223. package/lib/module/organisms/header/native-subtitle.types.js.map +1 -0
  224. package/lib/module/organisms/header/sheet-grabber.android.js +5 -0
  225. package/lib/module/organisms/header/sheet-grabber.android.js.map +1 -0
  226. package/lib/module/organisms/header/sheet-grabber.ios.js +15 -0
  227. package/lib/module/organisms/header/sheet-grabber.ios.js.map +1 -0
  228. package/lib/module/organisms/header/sheet-grabber.types.js +4 -0
  229. package/lib/module/organisms/header/sheet-grabber.types.js.map +1 -0
  230. package/lib/module/organisms/header/use-header-bar-inset.android.js +4 -0
  231. package/lib/module/organisms/header/use-header-bar-inset.android.js.map +1 -0
  232. package/lib/module/organisms/header/use-header-bar-inset.ios.js +9 -0
  233. package/lib/module/organisms/header/use-header-bar-inset.ios.js.map +1 -0
  234. package/lib/module/organisms/header/use-header-bar-inset.types.js +2 -0
  235. package/lib/module/organisms/header/use-header-bar-inset.types.js.map +1 -0
  236. package/lib/module/organisms/header/use-header-scroll-to-top.js +51 -0
  237. package/lib/module/organisms/header/use-header-scroll-to-top.js.map +1 -0
  238. package/lib/module/organisms/header/use-header-search.js +135 -0
  239. package/lib/module/organisms/header/use-header-search.js.map +1 -0
  240. package/lib/module/organisms/header/use-header.js +74 -0
  241. package/lib/module/organisms/header/use-header.js.map +1 -0
  242. package/lib/module/organisms/header/use-hero-header.js +189 -0
  243. package/lib/module/organisms/header/use-hero-header.js.map +1 -0
  244. package/lib/module/organisms/header/use-stack-screen-options.js +77 -0
  245. package/lib/module/organisms/header/use-stack-screen-options.js.map +1 -0
  246. package/lib/module/organisms/menu/menu.android.js +2 -2
  247. package/lib/module/organisms/menu/menu.android.js.map +1 -1
  248. package/lib/module/organisms/menu/menu.ios.js +1 -1
  249. package/lib/module/organisms/menu/menu.ios.js.map +1 -1
  250. package/lib/module/organisms/sheet/use-sheet-header.js +3 -1
  251. package/lib/module/organisms/sheet/use-sheet-header.js.map +1 -1
  252. package/lib/module/organisms/snackbar/snackbar.js +1 -1
  253. package/lib/module/organisms/snackbar/snackbar.js.map +1 -1
  254. package/lib/module/organisms/swipe-actions/swipe-actions.android.js +1 -1
  255. package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
  256. package/lib/module/organisms/swipe-actions/swipe-actions.ios.js +1 -1
  257. package/lib/module/organisms/swipe-actions/swipe-actions.ios.js.map +1 -1
  258. package/lib/module/templates/screen/screen.android.js +3 -3
  259. package/lib/module/templates/screen/screen.android.js.map +1 -1
  260. package/lib/module/templates/screen/screen.ios.js +3 -3
  261. package/lib/module/templates/screen/screen.ios.js.map +1 -1
  262. package/lib/module/utils/ripple-clip.js.map +1 -0
  263. package/lib/module/utils/tap-modifiers.js.map +1 -0
  264. package/lib/typescript/atoms/blur-view/blur-view.types.d.ts +8 -0
  265. package/lib/typescript/atoms/island/island.types.d.ts +17 -0
  266. package/lib/typescript/molecules/navigation-bar/navigation-bar.constants.d.ts +2 -0
  267. package/lib/typescript/molecules/navigation-bar/navigation-bar.types.d.ts +67 -4
  268. package/lib/typescript/organisms/header/header-color.d.ts +6 -0
  269. package/lib/typescript/organisms/header/header-inset.d.ts +1 -0
  270. package/lib/typescript/organisms/header/header-inset.ios.d.ts +2 -0
  271. package/lib/typescript/organisms/header/header-large-title-fade.d.ts +1 -0
  272. package/lib/typescript/organisms/header/header-large-title-fade.ios.d.ts +7 -0
  273. package/lib/typescript/organisms/header/header-options.d.ts +1 -0
  274. package/lib/typescript/organisms/header/header-options.ios.d.ts +2 -0
  275. package/lib/typescript/organisms/header/header-options.types.d.ts +4 -0
  276. package/lib/typescript/organisms/header/header-search-accessory.d.ts +1 -0
  277. package/lib/typescript/organisms/header/header-search-accessory.ios.d.ts +6 -0
  278. package/lib/typescript/organisms/header/header-search-accessory.types.d.ts +9 -0
  279. package/lib/typescript/organisms/header/header-search-input.d.ts +1 -0
  280. package/lib/typescript/organisms/header/header-search-input.ios.d.ts +5 -0
  281. package/lib/typescript/organisms/header/header-search-input.types.d.ts +17 -0
  282. package/lib/typescript/organisms/header/header-search-scrim.d.ts +11 -0
  283. package/lib/typescript/organisms/header/header-title.d.ts +22 -0
  284. package/lib/typescript/organisms/header/header.types.d.ts +445 -0
  285. package/lib/typescript/organisms/header/hero-header-background.d.ts +1 -0
  286. package/lib/typescript/organisms/header/hero-header-background.ios.d.ts +7 -0
  287. package/lib/typescript/organisms/header/hero-header-background.types.d.ts +9 -0
  288. package/lib/typescript/organisms/header/index.d.ts +12 -0
  289. package/lib/typescript/organisms/header/native-subtitle.d.ts +1 -0
  290. package/lib/typescript/organisms/header/native-subtitle.ios.d.ts +4 -0
  291. package/lib/typescript/organisms/header/native-subtitle.types.d.ts +6 -0
  292. package/lib/typescript/organisms/header/sheet-grabber.d.ts +1 -0
  293. package/lib/typescript/organisms/header/sheet-grabber.ios.d.ts +7 -0
  294. package/lib/typescript/organisms/header/sheet-grabber.types.d.ts +6 -0
  295. package/lib/typescript/organisms/header/use-header-bar-inset.d.ts +1 -0
  296. package/lib/typescript/organisms/header/use-header-bar-inset.ios.d.ts +1 -0
  297. package/lib/typescript/organisms/header/use-header-bar-inset.types.d.ts +6 -0
  298. package/lib/typescript/organisms/header/use-header-scroll-to-top.d.ts +8 -0
  299. package/lib/typescript/organisms/header/use-header-search.d.ts +12 -0
  300. package/lib/typescript/organisms/header/use-header.d.ts +14 -0
  301. package/lib/typescript/organisms/header/use-hero-header.d.ts +10 -0
  302. package/lib/typescript/organisms/header/use-stack-screen-options.d.ts +17 -0
  303. package/lib/typescript/templates/screen/screen.types.d.ts +6 -2
  304. package/package.json +24 -5
  305. package/src/atoms/activity-indicator/activity-indicator.android.tsx +1 -1
  306. package/src/atoms/activity-indicator/activity-indicator.ios.tsx +1 -1
  307. package/src/atoms/badge/badge.android.tsx +1 -1
  308. package/src/atoms/badge/badge.ios.tsx +1 -1
  309. package/src/atoms/blur-view/blur-view.android.tsx +28 -1
  310. package/src/atoms/blur-view/blur-view.ios.tsx +25 -0
  311. package/src/atoms/blur-view/blur-view.types.ts +8 -0
  312. package/src/atoms/button/button.android.tsx +1 -1
  313. package/src/atoms/button/button.ios.tsx +1 -1
  314. package/src/atoms/card/card.android.tsx +1 -1
  315. package/src/atoms/card/card.ios.tsx +1 -1
  316. package/src/atoms/checkbox/checkbox.android.tsx +1 -1
  317. package/src/atoms/checkbox/checkbox.ios.tsx +1 -1
  318. package/src/atoms/color-picker/color-picker.android.tsx +1 -1
  319. package/src/atoms/color-picker/color-picker.ios.tsx +1 -1
  320. package/src/atoms/date-picker/date-picker.android.tsx +1 -1
  321. package/src/atoms/date-picker/date-picker.ios.tsx +1 -1
  322. package/src/atoms/divider/divider.android.tsx +1 -1
  323. package/src/atoms/divider/divider.ios.tsx +1 -1
  324. package/src/atoms/fab/fab.android.tsx +1 -1
  325. package/src/atoms/fab/fab.ios.tsx +1 -1
  326. package/src/atoms/h-stack/h-stack.android.tsx +2 -2
  327. package/src/atoms/h-stack/h-stack.ios.tsx +2 -2
  328. package/src/atoms/icon/icon.android.tsx +1 -1
  329. package/src/atoms/icon/icon.ios.tsx +1 -1
  330. package/src/atoms/icon-button/icon-button.android.tsx +1 -1
  331. package/src/atoms/icon-button/icon-button.ios.tsx +1 -1
  332. package/src/atoms/island/island.android.tsx +3 -1
  333. package/src/atoms/island/island.ios.tsx +10 -2
  334. package/src/atoms/island/island.types.ts +18 -0
  335. package/src/atoms/list/list.android.tsx +2 -2
  336. package/src/atoms/list/list.ios.tsx +1 -1
  337. package/src/atoms/picker/picker.android.tsx +2 -3
  338. package/src/atoms/picker/picker.ios.tsx +1 -1
  339. package/src/atoms/pressable/pressable.tsx +1 -1
  340. package/src/atoms/progress/progress.android.tsx +1 -1
  341. package/src/atoms/progress/progress.ios.tsx +1 -1
  342. package/src/atoms/scroll-view/scroll-view.android.tsx +2 -2
  343. package/src/atoms/scroll-view/scroll-view.ios.tsx +1 -1
  344. package/src/atoms/segmented-control/segmented-control.android.tsx +1 -1
  345. package/src/atoms/segmented-control/segmented-control.ios.tsx +1 -1
  346. package/src/atoms/slider/slider.android.tsx +1 -1
  347. package/src/atoms/slider/slider.ios.tsx +1 -1
  348. package/src/atoms/spacer/spacer.android.tsx +1 -1
  349. package/src/atoms/spacer/spacer.ios.tsx +1 -1
  350. package/src/atoms/surface/surface.android.tsx +1 -1
  351. package/src/atoms/surface/surface.ios.tsx +1 -1
  352. package/src/atoms/switch/switch.android.tsx +1 -1
  353. package/src/atoms/switch/switch.ios.tsx +1 -1
  354. package/src/atoms/text/text.android.tsx +1 -1
  355. package/src/atoms/text/text.ios.tsx +1 -1
  356. package/src/atoms/text-field/text-field.android.tsx +2 -2
  357. package/src/atoms/text-field/text-field.ios.tsx +1 -1
  358. package/src/atoms/v-stack/v-stack.android.tsx +2 -2
  359. package/src/atoms/v-stack/v-stack.ios.tsx +2 -2
  360. package/src/atoms/z-stack/z-stack.android.tsx +2 -2
  361. package/src/atoms/z-stack/z-stack.ios.tsx +2 -2
  362. package/src/molecules/collapsible/collapsible.android.tsx +2 -2
  363. package/src/molecules/collapsible/collapsible.ios.tsx +1 -1
  364. package/src/molecules/labeled-content/labeled-content.tsx +1 -1
  365. package/src/molecules/list-item/list-item.tsx +1 -2
  366. package/src/molecules/navigation-bar/navigation-bar.constants.ts +2 -0
  367. package/src/molecules/navigation-bar/navigation-bar.ios.tsx +35 -5
  368. package/src/molecules/navigation-bar/navigation-bar.types.ts +69 -5
  369. package/src/molecules/pager/pager.android.tsx +1 -1
  370. package/src/molecules/pager/pager.ios.tsx +1 -1
  371. package/src/molecules/radio-group/radio-group.android.tsx +1 -1
  372. package/src/molecules/radio-group/radio-group.ios.tsx +2 -2
  373. package/src/molecules/rn-list-item/rn-list-item.tsx +1 -1
  374. package/src/molecules/rn-tooltip/rn-tooltip.android.tsx +1 -1
  375. package/src/molecules/search-bar/search-bar.tsx +1 -1
  376. package/src/molecules/stepper/stepper.android.tsx +2 -2
  377. package/src/molecules/stepper/stepper.ios.tsx +1 -1
  378. package/src/molecules/tooltip/tooltip.android.tsx +1 -1
  379. package/src/molecules/tooltip/tooltip.ios.tsx +1 -1
  380. package/src/organisms/action-sheet/action-sheet.android.tsx +1 -1
  381. package/src/organisms/action-sheet/action-sheet.ios.tsx +1 -1
  382. package/src/organisms/alert/alert.android.tsx +1 -1
  383. package/src/organisms/alert/alert.ios.tsx +1 -1
  384. package/src/organisms/context-menu/context-menu.android.tsx +2 -2
  385. package/src/organisms/fab-menu/fab-menu.android.tsx +2 -2
  386. package/src/organisms/fab-menu/fab-menu.ios.tsx +2 -3
  387. package/src/organisms/form/form.android.tsx +1 -1
  388. package/src/organisms/form/form.ios.tsx +1 -1
  389. package/src/organisms/header/header-color.ts +7 -0
  390. package/src/organisms/header/header-inset.android.tsx +3 -0
  391. package/src/organisms/header/header-inset.ios.tsx +12 -0
  392. package/src/organisms/header/header-large-title-fade.android.tsx +3 -0
  393. package/src/organisms/header/header-large-title-fade.ios.tsx +12 -0
  394. package/src/organisms/header/header-options.android.tsx +168 -0
  395. package/src/organisms/header/header-options.ios.tsx +138 -0
  396. package/src/organisms/header/header-options.types.ts +6 -0
  397. package/src/organisms/header/header-search-accessory.android.tsx +4 -0
  398. package/src/organisms/header/header-search-accessory.ios.tsx +42 -0
  399. package/src/organisms/header/header-search-accessory.types.ts +12 -0
  400. package/src/organisms/header/header-search-input.android.tsx +89 -0
  401. package/src/organisms/header/header-search-input.ios.tsx +7 -0
  402. package/src/organisms/header/header-search-input.types.ts +21 -0
  403. package/src/organisms/header/header-search-scrim.tsx +49 -0
  404. package/src/organisms/header/header-title.tsx +220 -0
  405. package/src/organisms/header/header.types.ts +423 -0
  406. package/src/organisms/header/hero-header-background.android.tsx +22 -0
  407. package/src/organisms/header/hero-header-background.ios.tsx +26 -0
  408. package/src/organisms/header/hero-header-background.types.ts +11 -0
  409. package/src/organisms/header/index.ts +12 -0
  410. package/src/organisms/header/native-subtitle.android.tsx +5 -0
  411. package/src/organisms/header/native-subtitle.ios.tsx +22 -0
  412. package/src/organisms/header/native-subtitle.types.ts +8 -0
  413. package/src/organisms/header/sheet-grabber.android.tsx +4 -0
  414. package/src/organisms/header/sheet-grabber.ios.tsx +12 -0
  415. package/src/organisms/header/sheet-grabber.types.ts +8 -0
  416. package/src/organisms/header/use-header-bar-inset.android.ts +1 -0
  417. package/src/organisms/header/use-header-bar-inset.ios.ts +7 -0
  418. package/src/organisms/header/use-header-bar-inset.types.ts +6 -0
  419. package/src/organisms/header/use-header-scroll-to-top.ts +48 -0
  420. package/src/organisms/header/use-header-search.tsx +138 -0
  421. package/src/organisms/header/use-header.ts +67 -0
  422. package/src/organisms/header/use-hero-header.tsx +222 -0
  423. package/src/organisms/header/use-stack-screen-options.ts +66 -0
  424. package/src/organisms/menu/menu.android.tsx +2 -2
  425. package/src/organisms/menu/menu.ios.tsx +1 -1
  426. package/src/organisms/sheet/use-sheet-header.tsx +4 -1
  427. package/src/organisms/snackbar/snackbar.tsx +1 -1
  428. package/src/organisms/swipe-actions/swipe-actions.android.tsx +1 -1
  429. package/src/organisms/swipe-actions/swipe-actions.ios.tsx +1 -1
  430. package/src/templates/screen/screen.android.tsx +8 -3
  431. package/src/templates/screen/screen.ios.tsx +8 -3
  432. package/src/templates/screen/screen.types.ts +7 -2
  433. package/lib/module/glyph/glyph-button.android.js.map +0 -1
  434. package/lib/module/glyph/glyph-button.ios.js.map +0 -1
  435. package/lib/module/glyph/glyph-button.types.js.map +0 -1
  436. package/lib/module/glyph/glyph.android.js.map +0 -1
  437. package/lib/module/glyph/glyph.ios.js.map +0 -1
  438. package/lib/module/glyph/glyph.types.js.map +0 -1
  439. package/lib/module/glyph/index.js.map +0 -1
  440. package/lib/module/glyph/rn-glyph.android.js.map +0 -1
  441. package/lib/module/glyph/rn-glyph.ios.js.map +0 -1
  442. package/lib/module/host-context.js.map +0 -1
  443. package/lib/module/ripple-clip.js.map +0 -1
  444. package/lib/module/tap-modifiers.js.map +0 -1
  445. package/lib/module/use-refresh-state.js.map +0 -1
  446. package/lib/module/use-stack-modifiers.js.map +0 -1
  447. /package/lib/module/{glyph → atoms/glyph}/glyph-button.android.js +0 -0
  448. /package/lib/module/{glyph → atoms/glyph}/glyph-button.ios.js +0 -0
  449. /package/lib/module/{glyph → atoms/glyph}/glyph-button.types.js +0 -0
  450. /package/lib/module/{glyph → atoms/glyph}/glyph.android.js +0 -0
  451. /package/lib/module/{glyph → atoms/glyph}/glyph.ios.js +0 -0
  452. /package/lib/module/{glyph → atoms/glyph}/glyph.types.js +0 -0
  453. /package/lib/module/{glyph → atoms/glyph}/index.js +0 -0
  454. /package/lib/module/{glyph → atoms/glyph}/rn-glyph.android.js +0 -0
  455. /package/lib/module/{glyph → atoms/glyph}/rn-glyph.ios.js +0 -0
  456. /package/lib/module/{host-context.js → hooks/host-context.js} +0 -0
  457. /package/lib/module/{use-refresh-state.js → hooks/use-refresh-state.js} +0 -0
  458. /package/lib/module/{use-stack-modifiers.js → hooks/use-stack-modifiers.js} +0 -0
  459. /package/lib/module/{ripple-clip.js → utils/ripple-clip.js} +0 -0
  460. /package/lib/module/{tap-modifiers.js → utils/tap-modifiers.js} +0 -0
  461. /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.d.ts +0 -0
  462. /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.ios.d.ts +0 -0
  463. /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.types.d.ts +0 -0
  464. /package/lib/typescript/{glyph → atoms/glyph}/glyph.d.ts +0 -0
  465. /package/lib/typescript/{glyph → atoms/glyph}/glyph.ios.d.ts +0 -0
  466. /package/lib/typescript/{glyph → atoms/glyph}/glyph.types.d.ts +0 -0
  467. /package/lib/typescript/{glyph → atoms/glyph}/index.d.ts +0 -0
  468. /package/lib/typescript/{glyph → atoms/glyph}/rn-glyph.d.ts +0 -0
  469. /package/lib/typescript/{glyph → atoms/glyph}/rn-glyph.ios.d.ts +0 -0
  470. /package/lib/typescript/{host-context.d.ts → hooks/host-context.d.ts} +0 -0
  471. /package/lib/typescript/{use-refresh-state.d.ts → hooks/use-refresh-state.d.ts} +0 -0
  472. /package/lib/typescript/{use-stack-modifiers.d.ts → hooks/use-stack-modifiers.d.ts} +0 -0
  473. /package/lib/typescript/{ripple-clip.d.ts → utils/ripple-clip.d.ts} +0 -0
  474. /package/lib/typescript/{tap-modifiers.d.ts → utils/tap-modifiers.d.ts} +0 -0
  475. /package/src/{glyph → atoms/glyph}/glyph-button.android.tsx +0 -0
  476. /package/src/{glyph → atoms/glyph}/glyph-button.ios.tsx +0 -0
  477. /package/src/{glyph → atoms/glyph}/glyph-button.types.ts +0 -0
  478. /package/src/{glyph → atoms/glyph}/glyph.android.tsx +0 -0
  479. /package/src/{glyph → atoms/glyph}/glyph.ios.tsx +0 -0
  480. /package/src/{glyph → atoms/glyph}/glyph.types.ts +0 -0
  481. /package/src/{glyph → atoms/glyph}/index.ts +0 -0
  482. /package/src/{glyph → atoms/glyph}/rn-glyph.android.tsx +0 -0
  483. /package/src/{glyph → atoms/glyph}/rn-glyph.ios.tsx +0 -0
  484. /package/src/{host-context.tsx → hooks/host-context.tsx} +0 -0
  485. /package/src/{use-refresh-state.ts → hooks/use-refresh-state.ts} +0 -0
  486. /package/src/{use-stack-modifiers.ts → hooks/use-stack-modifiers.ts} +0 -0
  487. /package/src/{ripple-clip.ts → utils/ripple-clip.ts} +0 -0
  488. /package/src/{tap-modifiers.ts → utils/tap-modifiers.ts} +0 -0
@@ -0,0 +1,11 @@
1
+ import type {ReactNode} from 'react'
2
+ import type {SharedValue} from 'react-native-reanimated'
3
+
4
+ export type HeroHeaderBackgroundProps = {
5
+ /** The bar's height, status bar included. */
6
+ height: number
7
+ /** How far the header has faded in: 0 over the hero, 1 revealed. */
8
+ progress: SharedValue<number>
9
+ }
10
+
11
+ export type HeroHeaderBackgroundComponent = (props: HeroHeaderBackgroundProps) => ReactNode
@@ -0,0 +1,12 @@
1
+ export * from './header.types'
2
+ export * from './header-inset'
3
+ export * from './header-large-title-fade'
4
+ export {useIsTitleRevealed} from './header-title'
5
+ export * from './sheet-grabber'
6
+ export type {SheetGrabberProps} from './sheet-grabber.types'
7
+ export * from './use-header'
8
+ export * from './use-header-bar-inset'
9
+ export * from './use-header-scroll-to-top'
10
+ export * from './use-header-search'
11
+ export * from './use-hero-header'
12
+ export * from './use-stack-screen-options'
@@ -0,0 +1,5 @@
1
+ import type {NativeSubtitleComponent} from './native-subtitle.types'
2
+
3
+ export const hasNativeSubtitle = false
4
+
5
+ export const NativeSubtitle: NativeSubtitleComponent = () => null
@@ -0,0 +1,22 @@
1
+ import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
2
+ import {NavigationItemSubtitle} from '@hzblj/kara-native/swift-ui'
3
+ import {useAppTheme} from '@hzblj/kara-theme'
4
+
5
+ import type {NativeSubtitleComponent, NativeSubtitleProps} from './native-subtitle.types'
6
+
7
+ /** iOS 26 puts a title and its subtitle on the leading side itself (`UINavigationItem.style = .browser`). */
8
+ export const hasNativeSubtitle = isScrollEdgeEffectAvailable()
9
+
10
+ export const NativeSubtitle: NativeSubtitleComponent = ({subtitle, subtitleHidden}: NativeSubtitleProps) => {
11
+ const theme = useAppTheme()
12
+
13
+ return (
14
+ <NavigationItemSubtitle
15
+ color={theme.color('content.secondary')}
16
+ fontFamily={theme.typography.label.fontFamily}
17
+ fontSize={theme.typography.label.fontSize}
18
+ subtitle={subtitle}
19
+ subtitleHidden={subtitleHidden}
20
+ />
21
+ )
22
+ }
@@ -0,0 +1,8 @@
1
+ import type {ReactNode} from 'react'
2
+
3
+ export type NativeSubtitleProps = {
4
+ subtitle: string
5
+ subtitleHidden: boolean
6
+ }
7
+
8
+ export type NativeSubtitleComponent = (props: NativeSubtitleProps) => ReactNode
@@ -0,0 +1,4 @@
1
+ import type {SheetGrabberComponent} from './sheet-grabber.types'
2
+
3
+ /** Android's modal is a full screen with no grabber. */
4
+ export const SheetGrabber: SheetGrabberComponent = () => null
@@ -0,0 +1,12 @@
1
+ import {SheetGrabber as NativeSheetGrabber} from '@hzblj/kara-native/swift-ui'
2
+
3
+ import type {SheetGrabberComponent, SheetGrabberProps} from './sheet-grabber.types'
4
+
5
+ /**
6
+ * The system grabber of a native-stack screen presented as a sheet (`presentation: 'modal'`), which react-native-screens
7
+ * gives only `formSheet`. Render it anywhere in the presented content, a nested stack's screens included; it takes no
8
+ * room. Android draws no grabber on a full-screen modal, and this renders nothing there.
9
+ */
10
+ export const SheetGrabber: SheetGrabberComponent = ({visible = true}: SheetGrabberProps) => (
11
+ <NativeSheetGrabber grabberVisible={visible} />
12
+ )
@@ -0,0 +1,8 @@
1
+ import type {ReactNode} from 'react'
2
+
3
+ export type SheetGrabberProps = {
4
+ /** Shows the grabber. `true` by default. */
5
+ visible?: boolean
6
+ }
7
+
8
+ export type SheetGrabberComponent = (props: SheetGrabberProps) => ReactNode
@@ -0,0 +1 @@
1
+ export const useHeaderBarInset = () => 0
@@ -0,0 +1,7 @@
1
+ import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
2
+ import {useSafeAreaInsets} from 'react-native-safe-area-context'
3
+
4
+ /** iOS 26's navigation bar is 54pt, the 44pt row of items plus room for its glass; before it the bar was the row. */
5
+ const BAR_HEIGHT = isScrollEdgeEffectAvailable() ? 54 : 44
6
+
7
+ export const useHeaderBarInset = () => useSafeAreaInsets().top + BAR_HEIGHT
@@ -0,0 +1,6 @@
1
+ /**
2
+ * How far a transparent header reaches into the screen: the status bar plus the bar's own height on iOS, 0 on Android,
3
+ * where the top app bar sits above the content. Known on the first render, unlike a measured header height, so content
4
+ * laid out by it never jumps once the bar reports its size.
5
+ */
6
+ export type UseHeaderBarInset = () => number
@@ -0,0 +1,48 @@
1
+ import {type RefObject, useMemo} from 'react'
2
+ import {Platform} from 'react-native'
3
+
4
+ import type {HeaderScrollable, HeaderScrollToTopOptions, HeaderScrollToTopRef} from './header.types'
5
+ import {useHeaderBarInset} from './use-header-bar-inset'
6
+
7
+ /** The row iOS gives a large title, which the top of a large-title screen includes. */
8
+ const LARGE_TITLE_HEIGHT = 52
9
+
10
+ const scrollToOffset = (target: HeaderScrollable, offset: number) => {
11
+ if ('scrollToOffset' in target) {
12
+ target.scrollToOffset({animated: true, offset})
13
+ return
14
+ }
15
+
16
+ if ('scrollTo' in target) {
17
+ target.scrollTo({animated: true, y: offset})
18
+ return
19
+ }
20
+
21
+ target.getScrollResponder()?.scrollTo({animated: true, y: offset})
22
+ }
23
+
24
+ /**
25
+ * A ref for React Navigation's or Expo Router's `useScrollToTop`: pressing the tab again scrolls the list back under a
26
+ * transparent header, with a large title expanded, where scrolling to 0 would leave the content under the bar and the
27
+ * title collapsed. `useScrollToTop(useHeaderScrollToTop(listRef, {largeTitle: true}))`.
28
+ */
29
+ export const useHeaderScrollToTop = (
30
+ ref: RefObject<HeaderScrollable | null>,
31
+ {largeTitle = false, topInset = 0}: HeaderScrollToTopOptions = {}
32
+ ): HeaderScrollToTopRef => {
33
+ const barInset = useHeaderBarInset()
34
+ const offset = Platform.OS === 'ios' ? -(barInset + (largeTitle ? LARGE_TITLE_HEIGHT : 0) + topInset) : 0
35
+
36
+ return useMemo(
37
+ () => ({
38
+ current: {
39
+ scrollToTop: () => {
40
+ if (ref.current) {
41
+ scrollToOffset(ref.current, offset)
42
+ }
43
+ },
44
+ },
45
+ }),
46
+ [offset, ref]
47
+ )
48
+ }
@@ -0,0 +1,138 @@
1
+ import {useAppTheme} from '@hzblj/kara-theme'
2
+ import {useCallback, useMemo, useRef, useState} from 'react'
3
+ import {Platform} from 'react-native'
4
+ import type {SearchBarCommands} from 'react-native-screens'
5
+
6
+ import type {HeaderSearch, HeaderSearchBarOptions, HeaderSearchParams, HeaderSearchPlacement} from './header.types'
7
+ import {headerColor} from './header-color'
8
+ import {HeaderSearchAccessory} from './header-search-accessory'
9
+ import {HEADER_SEARCH_ROW_HEIGHT, HeaderSearchInput} from './header-search-input'
10
+ import type {HeaderSearchInputRef} from './header-search-input.types'
11
+ import {HeaderSearchScrim} from './header-search-scrim'
12
+
13
+ /** `integratedButton` hides the title and items while the field is open and keeps it out of the bottom toolbar. */
14
+ const PLACEMENT_OPTIONS: Record<HeaderSearchPlacement, Partial<HeaderSearchBarOptions>> = {
15
+ integratedButton: {allowToolbarIntegration: false, hideNavigationBar: true},
16
+ stacked: {hideNavigationBar: false},
17
+ }
18
+
19
+ /**
20
+ * A search field for a screen's header. iOS: UIKit's search bar stacked under the title, with the title and items kept
21
+ * and nothing dimmed by the system, so a list can filter live, or behind a search button in the bar's corner
22
+ * (`placement`); pass `searchBar` to `useHeader` as `search`. Android: Material's search field with its cancel button,
23
+ * as a row for the screen to put right under the top app bar (`searchInput`). Both report the `query`; `scrim` washes
24
+ * the content out while the field is focused and empty and, on iOS, carries the field's glyphs and voice.
25
+ *
26
+ * With `voice` the field has a microphone; the app runs the dictation and writes what it hears with `setText`. Typing
27
+ * over it, or ending the search, calls `voice.onCancel`.
28
+ */
29
+ export const useHeaderSearch = ({
30
+ cancelIcon,
31
+ clearIcon,
32
+ enabled = true,
33
+ placeholder = 'Search',
34
+ placement = 'stacked',
35
+ voice,
36
+ }: HeaderSearchParams = {}): HeaderSearch => {
37
+ const theme = useAppTheme()
38
+ const [query, setQuery] = useState('')
39
+ const [focused, setFocused] = useState(false)
40
+ const searchBarRef = useRef<SearchBarCommands | null>(null)
41
+ const inputRef = useRef<HeaderSearchInputRef>(null)
42
+ const lastSetText = useRef<string | null>(null)
43
+ const voiceRef = useRef(voice)
44
+
45
+ voiceRef.current = voice
46
+
47
+ const cancelVoice = useCallback(() => {
48
+ if (voiceRef.current?.listening) {
49
+ voiceRef.current.onCancel?.()
50
+ }
51
+ }, [])
52
+
53
+ const changeQuery = useCallback(
54
+ (text: string) => {
55
+ if (text !== lastSetText.current) {
56
+ cancelVoice()
57
+ }
58
+ setQuery(text)
59
+ },
60
+ [cancelVoice]
61
+ )
62
+
63
+ const reset = useCallback(() => {
64
+ cancelVoice()
65
+ lastSetText.current = null
66
+ setQuery('')
67
+ setFocused(false)
68
+ }, [cancelVoice])
69
+
70
+ const searchBar = useMemo((): HeaderSearchBarOptions | undefined => {
71
+ if (!enabled || Platform.OS !== 'ios') {
72
+ return undefined
73
+ }
74
+
75
+ return {
76
+ ...PLACEMENT_OPTIONS[placement],
77
+ autoCapitalize: 'none',
78
+ hideWhenScrolling: false,
79
+ obscureBackground: false,
80
+ onBlur: () => setFocused(false),
81
+ onCancelButtonPress: reset,
82
+ onChangeText: event => changeQuery(event.nativeEvent.text),
83
+ onFocus: () => setFocused(true),
84
+ placeholder,
85
+ placement,
86
+ ref: searchBarRef,
87
+ tintColor: headerColor(theme, 'content.primary'),
88
+ }
89
+ }, [changeQuery, enabled, placeholder, placement, reset, theme])
90
+
91
+ const searchInput = useMemo(() => {
92
+ if (!enabled || Platform.OS !== 'android') {
93
+ return null
94
+ }
95
+
96
+ return (
97
+ <HeaderSearchInput
98
+ clearIcon={clearIcon}
99
+ onChangeText={changeQuery}
100
+ onFocusChange={setFocused}
101
+ placeholder={placeholder}
102
+ ref={inputRef}
103
+ voice={voice}
104
+ />
105
+ )
106
+ }, [changeQuery, clearIcon, enabled, placeholder, voice])
107
+
108
+ const clear = useCallback(() => {
109
+ reset()
110
+ searchBarRef.current?.cancelSearch()
111
+ inputRef.current?.clear()
112
+ inputRef.current?.blur()
113
+ }, [reset])
114
+
115
+ const setText = useCallback((text: string) => {
116
+ lastSetText.current = text
117
+ setQuery(text)
118
+ searchBarRef.current?.setText(text)
119
+ inputRef.current?.setText(text)
120
+ }, [])
121
+
122
+ const dimmed = enabled && focused && query.trim().length === 0
123
+
124
+ const scrim = useMemo(() => {
125
+ if (!enabled) {
126
+ return null
127
+ }
128
+
129
+ return (
130
+ <>
131
+ {dimmed ? <HeaderSearchScrim onPress={clear} topOffset={HEADER_SEARCH_ROW_HEIGHT} /> : null}
132
+ <HeaderSearchAccessory cancelIcon={cancelIcon} clearIcon={clearIcon} voice={voice} />
133
+ </>
134
+ )
135
+ }, [cancelIcon, clear, clearIcon, dimmed, enabled, voice])
136
+
137
+ return {clear, query, scrim, searchBar, searchInput, setText}
138
+ }
@@ -0,0 +1,67 @@
1
+ import {useAppTheme} from '@hzblj/kara-theme'
2
+ import {useLayoutEffect, useMemo} from 'react'
3
+
4
+ import type {HeaderContent, HeaderNavigation, HeaderOptions, HeaderOptionsResult, HeaderParams} from './header.types'
5
+ import {toHeaderOptions} from './header-options'
6
+ import {useHeaderTitle} from './header-title'
7
+
8
+ /**
9
+ * A screen's header as native-stack options, with what its list needs alongside: the title (centred, leading with a
10
+ * subtitle, revealed on scroll or large), the actions on both sides (the navigation bar's own items on iOS, Material
11
+ * buttons on Android) and a search field. Spread `options` into Expo Router's `<Stack.Screen options>` or a React
12
+ * Navigation screen's `options`. Memoize the action arrays: new arrays build new options on every render.
13
+ */
14
+ export const useHeaderOptions = ({
15
+ backHidden,
16
+ largeTitle,
17
+ leadingActions,
18
+ search,
19
+ subtitle,
20
+ title,
21
+ titleHidden = false,
22
+ titleRevealProgress,
23
+ trailingActions,
24
+ }: HeaderParams): HeaderOptionsResult => {
25
+ const theme = useAppTheme()
26
+ const headerTitle = useHeaderTitle({largeTitle, subtitle, title, titleHidden, titleRevealProgress})
27
+
28
+ const actionOptions = useMemo(
29
+ () => toHeaderOptions({backHidden, leadingActions, trailingActions}, theme),
30
+ [backHidden, leadingActions, theme, trailingActions]
31
+ )
32
+
33
+ return useMemo(() => {
34
+ const options: HeaderOptions = {}
35
+
36
+ Object.assign(options, headerTitle.options, actionOptions)
37
+
38
+ if (search) {
39
+ options.headerSearchBarOptions = search
40
+ }
41
+
42
+ return {
43
+ contentInsetAdjustmentBehavior: 'automatic',
44
+ largeTitle: headerTitle.largeTitle,
45
+ onScroll: headerTitle.onScroll,
46
+ options,
47
+ }
48
+ }, [actionOptions, headerTitle.largeTitle, headerTitle.onScroll, headerTitle.options, search])
49
+ }
50
+
51
+ /**
52
+ * Sets the screen's header through its navigation object, from React Navigation's or Expo Router's `useNavigation()`,
53
+ * before the screen first paints, and returns what its list needs. What a screen has from the moment it is pushed
54
+ * belongs in its static `options` too: iOS morphs the bar items between screens only when they exist at the push.
55
+ */
56
+ export const useHeader = (navigation: HeaderNavigation, params: HeaderParams): HeaderContent => {
57
+ const {contentInsetAdjustmentBehavior, largeTitle, onScroll, options} = useHeaderOptions(params)
58
+
59
+ useLayoutEffect(() => {
60
+ navigation.setOptions(options)
61
+ }, [navigation, options])
62
+
63
+ return useMemo(
64
+ () => ({contentInsetAdjustmentBehavior, largeTitle, onScroll}),
65
+ [contentInsetAdjustmentBehavior, largeTitle, onScroll]
66
+ )
67
+ }
@@ -0,0 +1,222 @@
1
+ import {isScrollEdgeEffectAvailable, isViewControllerBasedStatusBarAppearance} from '@hzblj/kara-native'
2
+ import {useAppTheme} from '@hzblj/kara-theme'
3
+ import {useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'
4
+ import {Platform, StatusBar} from 'react-native'
5
+ import {
6
+ Extrapolation,
7
+ interpolate,
8
+ type SharedValue,
9
+ useAnimatedReaction,
10
+ useDerivedValue,
11
+ useSharedValue,
12
+ } from 'react-native-reanimated'
13
+ import {useSafeAreaInsets} from 'react-native-safe-area-context'
14
+ import {scheduleOnRN} from 'react-native-worklets'
15
+
16
+ import type {
17
+ HeaderAction,
18
+ HeaderOptions,
19
+ HeroHeader,
20
+ HeroHeaderNavigation,
21
+ HeroHeaderParams,
22
+ HeroHeaderStyle,
23
+ } from './header.types'
24
+ import {headerColor} from './header-color'
25
+ import {useIsTitleRevealed} from './header-title'
26
+ import {HeroHeaderBackground} from './hero-header-background'
27
+ import {useHeaderOptions} from './use-header'
28
+ import {useHeaderBarInset} from './use-header-bar-inset'
29
+ import {HERO_HEADER_SCREEN_OPTIONS} from './use-stack-screen-options'
30
+
31
+ /** The header fades in while 50 to 70 % of the hero's part under the bar scrolls away. */
32
+ const REVEAL_RANGE = [0.5, 0.7] as const
33
+
34
+ /** Material 3's top app bar. */
35
+ const ANDROID_BAR_HEIGHT = 64
36
+
37
+ const useHeroBarHeight = () => {
38
+ const iosBarInset = useHeaderBarInset()
39
+ const {top} = useSafeAreaInsets()
40
+
41
+ return Platform.OS === 'ios' ? iosBarInset : top + ANDROID_BAR_HEIGHT
42
+ }
43
+
44
+ const useIsOverHero = (progress: SharedValue<number>) => {
45
+ const [overHero, setOverHero] = useState(() => progress.value === 0)
46
+
47
+ useAnimatedReaction(
48
+ () => progress.value === 0,
49
+ (isOver, wasOver) => {
50
+ if (isOver !== wasOver) {
51
+ scheduleOnRN(setOverHero, isOver)
52
+ }
53
+ }
54
+ )
55
+
56
+ return overHero
57
+ }
58
+
59
+ const useIsScreenFocused = (navigation: HeroHeaderNavigation) => {
60
+ const [focused, setFocused] = useState(() => navigation.isFocused())
61
+
62
+ useEffect(() => {
63
+ const unsubscribeFocus = navigation.addListener('focus', () => setFocused(true))
64
+ const unsubscribeBlur = navigation.addListener('blur', () => setFocused(false))
65
+
66
+ return () => {
67
+ unsubscribeFocus()
68
+ unsubscribeBlur()
69
+ }
70
+ }, [navigation])
71
+
72
+ return focused
73
+ }
74
+
75
+ const BAR_STYLES = {dark: 'dark-content', light: 'light-content'} as const satisfies Record<HeroHeaderStyle, string>
76
+
77
+ /**
78
+ * The status bar over the hero, for this screen only. With view-controller-based appearance (Info.plist) the native
79
+ * stack sets it per screen (`statusBarStyle`); otherwise, as in Expo apps and on Android, React Native's `StatusBar`
80
+ * applies it while the screen is focused and over the hero, and its stack restores the previous style once it is not.
81
+ */
82
+ const useHeroStatusBar = (navigation: HeroHeaderNavigation, overHero: boolean, overHeroStyle: HeroHeaderStyle) => {
83
+ const focused = useIsScreenFocused(navigation)
84
+ const perScreen = isViewControllerBasedStatusBarAppearance()
85
+
86
+ useEffect(() => {
87
+ if (perScreen) {
88
+ navigation.setOptions({statusBarStyle: overHero ? overHeroStyle : 'auto'})
89
+ }
90
+ }, [navigation, overHero, overHeroStyle, perScreen])
91
+
92
+ if (perScreen || !focused || !overHero) {
93
+ return null
94
+ }
95
+
96
+ return <StatusBar animated barStyle={BAR_STYLES[overHeroStyle]} />
97
+ }
98
+
99
+ const useRevealProgress = (barHeight: number, heroHeight: number, introProgress?: SharedValue<number>) => {
100
+ const scrollY = useSharedValue(0)
101
+ const heroUnderBar = heroHeight - barHeight
102
+ const start = heroUnderBar * REVEAL_RANGE[0]
103
+ const end = heroUnderBar * REVEAL_RANGE[1]
104
+
105
+ const scrollProgress = useDerivedValue(() => {
106
+ if (end <= start) {
107
+ return 1
108
+ }
109
+
110
+ return interpolate(scrollY.value, [start, end], [0, 1], Extrapolation.CLAMP)
111
+ })
112
+
113
+ const progress = useDerivedValue(() => Math.max(scrollProgress.value, introProgress?.value ?? 0))
114
+
115
+ const onScroll = useCallback(
116
+ (offsetY: number) => {
117
+ 'worklet'
118
+ scrollY.value = offsetY
119
+ },
120
+ [scrollY]
121
+ )
122
+
123
+ return {onScroll, progress, scrollProgress}
124
+ }
125
+
126
+ /**
127
+ * A header over a full-bleed hero: the content runs under a transparent bar, and while 50 to 70 % of the hero's part
128
+ * under the bar scrolls away the header fades in — the title (and subtitle) in the bar, the bar's background
129
+ * (`background`: iOS 26's scroll edge effect switched on, the bar's material coming in below iOS 26, the top app bar's
130
+ * surface on Android), the items' tint and the status bar, light over the hero with `overHeroStyle` `light`. `revealedTrailingActions` join the
131
+ * bar once the title is shown. Give the screen `HERO_HEADER_SCREEN_OPTIONS` as static options, so its first frame has
132
+ * no large title, render `background` after the list and call `onScroll` with its offset.
133
+ */
134
+ export const useHeroHeader = (
135
+ navigation: HeroHeaderNavigation,
136
+ {
137
+ backHidden,
138
+ heroHeight,
139
+ introProgress,
140
+ leadingActions,
141
+ overHeroStyle = 'light',
142
+ revealedTrailingActions,
143
+ subtitle,
144
+ title,
145
+ trailingActions,
146
+ }: HeroHeaderParams
147
+ ): HeroHeader => {
148
+ const theme = useAppTheme()
149
+ const barHeight = useHeroBarHeight()
150
+ const {onScroll, progress, scrollProgress} = useRevealProgress(barHeight, heroHeight, introProgress)
151
+ const titleShown = useIsTitleRevealed(scrollProgress)
152
+ const overHero = useIsOverHero(progress)
153
+ const scrolledOverHero = useIsOverHero(scrollProgress)
154
+ const statusBar = useHeroStatusBar(navigation, overHero, overHeroStyle)
155
+
156
+ const visibleTrailingActions = useMemo(() => {
157
+ if (!titleShown || !revealedTrailingActions) {
158
+ return trailingActions
159
+ }
160
+
161
+ const actions: HeaderAction[] = []
162
+
163
+ for (const action of revealedTrailingActions) {
164
+ actions.push(action)
165
+ }
166
+
167
+ for (const action of trailingActions ?? []) {
168
+ actions.push(action)
169
+ }
170
+
171
+ return actions
172
+ }, [revealedTrailingActions, titleShown, trailingActions])
173
+
174
+ const {options} = useHeaderOptions({
175
+ backHidden,
176
+ leadingActions,
177
+ subtitle,
178
+ title,
179
+ titleRevealProgress: scrollProgress,
180
+ trailingActions: visibleTrailingActions,
181
+ })
182
+
183
+ useLayoutEffect(() => {
184
+ navigation.setOptions(HERO_HEADER_SCREEN_OPTIONS)
185
+ }, [navigation])
186
+
187
+ useLayoutEffect(() => {
188
+ navigation.setOptions(options)
189
+ }, [navigation, options])
190
+
191
+ useEffect(() => {
192
+ const light = overHero && overHeroStyle === 'light'
193
+ const appearance: HeaderOptions = {
194
+ headerTintColor: headerColor(theme, light ? 'content.on.accent' : 'content.primary'),
195
+ }
196
+
197
+ if (isScrollEdgeEffectAvailable()) {
198
+ appearance.scrollEdgeEffects = {top: scrolledOverHero ? 'hidden' : 'automatic'}
199
+ }
200
+
201
+ navigation.setOptions(appearance)
202
+ }, [navigation, overHero, overHeroStyle, scrolledOverHero, theme])
203
+
204
+ const background = useMemo(
205
+ () => (
206
+ <>
207
+ <HeroHeaderBackground height={barHeight} progress={progress} />
208
+ {statusBar}
209
+ </>
210
+ ),
211
+ [barHeight, progress, statusBar]
212
+ )
213
+
214
+ return {
215
+ background,
216
+ contentInsetAdjustmentBehavior: 'never',
217
+ largeTitle: null,
218
+ onScroll,
219
+ progress,
220
+ scrollProgress,
221
+ }
222
+ }
@@ -0,0 +1,66 @@
1
+ import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
2
+ import {useAppTheme} from '@hzblj/kara-theme'
3
+ import {useMemo} from 'react'
4
+ import {Platform} from 'react-native'
5
+
6
+ import type {StackScreenOptions} from './header.types'
7
+ import {headerColor} from './header-color'
8
+
9
+ /** iOS 26 draws its own scroll edge effect under a transparent bar; older iOS needs the bar's blur instead. */
10
+ const HEADER_BLUR_EFFECT =
11
+ Platform.OS === 'ios' && !isScrollEdgeEffectAvailable() ? ('systemChromeMaterial' as const) : undefined
12
+
13
+ /**
14
+ * A native stack's `screenOptions` in the active theme: the surface behind every screen, a minimal back button, the
15
+ * title and large title in the theme's `headline` and `headingM`, no hairline under the bar. iOS keeps the bar
16
+ * transparent so iOS 26 blurs what scrolls under it (older iOS gets the bar's material); Android gets a solid top app
17
+ * bar in the surface colour.
18
+ */
19
+ export const useStackScreenOptions = (): StackScreenOptions => {
20
+ const theme = useAppTheme()
21
+
22
+ return useMemo(() => {
23
+ const content = headerColor(theme, 'content.primary')
24
+ const surface = headerColor(theme, 'surface.base')
25
+
26
+ return {
27
+ contentStyle: {backgroundColor: surface},
28
+ headerBackButtonDisplayMode: 'minimal',
29
+ headerBlurEffect: HEADER_BLUR_EFFECT,
30
+ headerLargeTitleShadowVisible: false,
31
+ headerLargeTitleStyle: {
32
+ color: content,
33
+ fontFamily: theme.typography.headingM.fontFamily,
34
+ fontSize: theme.typography.headingM.fontSize,
35
+ },
36
+ headerShadowVisible: false,
37
+ headerStyle: Platform.OS === 'android' ? {backgroundColor: surface} : undefined,
38
+ headerTintColor: content,
39
+ headerTitleStyle: {
40
+ color: content,
41
+ fontFamily: theme.typography.headline.fontFamily,
42
+ fontSize: theme.typography.headline.fontSize,
43
+ },
44
+ headerTransparent: Platform.OS === 'ios',
45
+ }
46
+ }, [theme])
47
+ }
48
+
49
+ /** A screen with a large title that collapses into the bar as its list scrolls. */
50
+ export const LARGE_TITLE_SCREEN_OPTIONS: StackScreenOptions = {headerLargeTitleEnabled: true, headerShown: true}
51
+
52
+ /** A screen without the stack's header, for one that draws its own or none. */
53
+ export const HIDDEN_HEADER_SCREEN_OPTIONS: StackScreenOptions = {headerShown: false}
54
+
55
+ /**
56
+ * A screen whose content runs under a transparent bar, a full-bleed image above all, for `useHeroHeader`. Static, so the
57
+ * first frame already has no large title and no bar background over the image.
58
+ */
59
+ export const HERO_HEADER_SCREEN_OPTIONS: StackScreenOptions = {
60
+ headerBlurEffect: 'none',
61
+ headerLargeTitleEnabled: false,
62
+ headerShown: true,
63
+ headerStyle: {backgroundColor: 'transparent'},
64
+ headerTransparent: true,
65
+ scrollEdgeEffects: {top: 'hidden'},
66
+ }
@@ -1,12 +1,12 @@
1
1
  import {Text as ComposeText, DropdownMenu, DropdownMenuItem} from '@expo/ui/jetpack-compose'
2
2
  import {useAppTheme} from '@hzblj/kara-theme'
3
3
  import {type ReactNode, useCallback, useState} from 'react'
4
+ import {Glyph} from '../../atoms/glyph'
4
5
  import {HStack} from '../../atoms/h-stack'
5
6
  import {Icon} from '../../atoms/icon'
6
7
  import {IconButton} from '../../atoms/icon-button'
7
8
  import {Text} from '../../atoms/text'
8
- import {Glyph} from '../../glyph'
9
- import {useHostGuard} from '../../host-context'
9
+ import {useHostGuard} from '../../hooks/host-context'
10
10
  import type {MenuAction, MenuComponent, MenuProps} from './menu.types'
11
11
  import {isIconOnly, menuActionColor} from './menu-parts'
12
12