@btndprojects/loam-ui-native 0.0.0-stage → 0.1.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 (269) hide show
  1. package/LICENSE +21 -0
  2. package/dist/accordion/index.js +3 -0
  3. package/dist/adaptive/index.js +2 -0
  4. package/dist/alert/index.js +3 -0
  5. package/dist/analyzer/index.js +6 -0
  6. package/dist/announcement/index.js +6 -0
  7. package/dist/audio-player/index.js +3 -0
  8. package/dist/avatar/index.js +3 -0
  9. package/dist/avatar-group/index.js +3 -0
  10. package/dist/badge/index.js +5 -0
  11. package/dist/banner/index.js +2 -0
  12. package/dist/board/index.js +7 -0
  13. package/dist/button/index.js +5 -0
  14. package/dist/camera-permission/index.js +13 -0
  15. package/dist/capture-result/index.js +18 -0
  16. package/dist/card/index.js +4 -0
  17. package/dist/card-form/index.js +10 -0
  18. package/dist/carousel/index.js +2 -0
  19. package/dist/chart/index.js +6 -0
  20. package/dist/charts/index.js +7 -0
  21. package/dist/checkbox/index.js +7 -0
  22. package/dist/checklist/index.js +10 -0
  23. package/dist/checkout/index.js +13 -0
  24. package/dist/chip/index.js +7 -0
  25. package/dist/chunk-223Q2T2Q.js +229 -0
  26. package/dist/chunk-2A5NIQAK.js +254 -0
  27. package/dist/chunk-2GK6PHGE.js +363 -0
  28. package/dist/chunk-2WUTXHMU.js +1 -0
  29. package/dist/chunk-2ZZ7TDJ4.js +324 -0
  30. package/dist/chunk-35OFI3JG.js +499 -0
  31. package/dist/chunk-3ARE4TES.js +195 -0
  32. package/dist/chunk-3ECPOSKO.js +1 -0
  33. package/dist/chunk-3ENTMPQY.js +229 -0
  34. package/dist/chunk-3HJH6FF4.js +98 -0
  35. package/dist/chunk-3NMD74ZU.js +4 -0
  36. package/dist/chunk-3WPW4KUN.js +182 -0
  37. package/dist/chunk-47FWHGUN.js +394 -0
  38. package/dist/chunk-4BXMXBJO.js +141 -0
  39. package/dist/chunk-4DS5ZWVC.js +83 -0
  40. package/dist/chunk-4GBOGOTV.js +295 -0
  41. package/dist/chunk-4SZYY6AN.js +99 -0
  42. package/dist/chunk-4YIP3CWS.js +237 -0
  43. package/dist/chunk-4YQOR7JM.js +204 -0
  44. package/dist/chunk-4ZAHCY44.js +177 -0
  45. package/dist/chunk-52X2VYTD.js +228 -0
  46. package/dist/chunk-5P26LBDL.js +1 -0
  47. package/dist/chunk-5XLRYDAX.js +1 -0
  48. package/dist/chunk-5YNTNN5A.js +177 -0
  49. package/dist/chunk-66SG2H23.js +129 -0
  50. package/dist/chunk-6HT2LP62.js +504 -0
  51. package/dist/chunk-6N33LFQA.js +35 -0
  52. package/dist/chunk-6X6PQ443.js +432 -0
  53. package/dist/chunk-7CFF4HXS.js +126 -0
  54. package/dist/chunk-7O2FMUPT.js +755 -0
  55. package/dist/chunk-AI4DWZCD.js +162 -0
  56. package/dist/chunk-AWJOCGFZ.js +213 -0
  57. package/dist/chunk-AZRX6SAC.js +285 -0
  58. package/dist/chunk-B54QE2FN.js +1 -0
  59. package/dist/chunk-BC475SJH.js +1 -0
  60. package/dist/chunk-BEDBFJND.js +202 -0
  61. package/dist/chunk-BTTFCMKG.js +166 -0
  62. package/dist/chunk-BV3B2HYZ.js +1635 -0
  63. package/dist/chunk-C2NTXVWM.js +98 -0
  64. package/dist/chunk-CDRZHT42.js +424 -0
  65. package/dist/chunk-CJ5EOYZE.js +62 -0
  66. package/dist/chunk-CUMUNPXY.js +128 -0
  67. package/dist/chunk-CUS3ZSCU.js +1 -0
  68. package/dist/chunk-CYRBHQQZ.js +411 -0
  69. package/dist/chunk-DHWADYZX.js +144 -0
  70. package/dist/chunk-DKCSPATL.js +1 -0
  71. package/dist/chunk-DM5QUKKO.js +326 -0
  72. package/dist/chunk-DSJBXLUG.js +845 -0
  73. package/dist/chunk-DYBH2I6W.js +1 -0
  74. package/dist/chunk-DZ255O2F.js +1 -0
  75. package/dist/chunk-E35BPSGW.js +1 -0
  76. package/dist/chunk-EJBDRVIH.js +166 -0
  77. package/dist/chunk-F43NP42L.js +1 -0
  78. package/dist/chunk-F5XNPWS7.js +1024 -0
  79. package/dist/chunk-FIF2M24N.js +1521 -0
  80. package/dist/chunk-FOKG57CR.js +993 -0
  81. package/dist/chunk-G3HOYSF7.js +1 -0
  82. package/dist/chunk-GDKZTW34.js +395 -0
  83. package/dist/chunk-GL7B2DOW.js +134 -0
  84. package/dist/chunk-GNNJ4GCC.js +471 -0
  85. package/dist/chunk-GNZ56EUI.js +1 -0
  86. package/dist/chunk-H6NIOQHK.js +346 -0
  87. package/dist/chunk-H6PC7RDA.js +1 -0
  88. package/dist/chunk-HPPW424V.js +334 -0
  89. package/dist/chunk-HQOQIBJI.js +1 -0
  90. package/dist/chunk-HU2EQNRZ.js +44 -0
  91. package/dist/chunk-IIQTAY3J.js +715 -0
  92. package/dist/chunk-ITTYJZWX.js +271 -0
  93. package/dist/chunk-IVNRBPGC.js +1018 -0
  94. package/dist/chunk-J4KLDGI4.js +1 -0
  95. package/dist/chunk-J7Q7VCVC.js +254 -0
  96. package/dist/chunk-JDEOITQ2.js +1 -0
  97. package/dist/chunk-JNH2TYIN.js +96 -0
  98. package/dist/chunk-JRLTGLN4.js +1 -0
  99. package/dist/chunk-JTRJJLKL.js +1 -0
  100. package/dist/chunk-JUAMLEHX.js +379 -0
  101. package/dist/chunk-JXJB2X6M.js +131 -0
  102. package/dist/chunk-KMI5ML5I.js +262 -0
  103. package/dist/chunk-KTAC4MGE.js +4 -0
  104. package/dist/chunk-LAECSR4H.js +178 -0
  105. package/dist/chunk-LK4BCSGX.js +1 -0
  106. package/dist/chunk-LOE5YNN3.js +470 -0
  107. package/dist/chunk-LRJNDEIA.js +302 -0
  108. package/dist/chunk-LSS54GLJ.js +116 -0
  109. package/dist/chunk-LZLHELAG.js +137 -0
  110. package/dist/chunk-M2LUXGKZ.js +1 -0
  111. package/dist/chunk-MOUJOP55.js +425 -0
  112. package/dist/chunk-N32RGOA5.js +159 -0
  113. package/dist/chunk-N5ZNE2YE.js +1 -0
  114. package/dist/chunk-NBNCBML6.js +1 -0
  115. package/dist/chunk-NKB7BZYK.js +167 -0
  116. package/dist/chunk-NQYBUO3K.js +1795 -0
  117. package/dist/chunk-NY2U3PBI.js +59 -0
  118. package/dist/chunk-O4AEHWII.js +464 -0
  119. package/dist/chunk-O7AE6DAF.js +146 -0
  120. package/dist/chunk-OETCUXQP.js +134 -0
  121. package/dist/chunk-OOPPQH27.js +179 -0
  122. package/dist/chunk-OPXFZ4Y5.js +204 -0
  123. package/dist/chunk-P23EQNRQ.js +338 -0
  124. package/dist/chunk-PJXUJYS6.js +248 -0
  125. package/dist/chunk-PNWUWOBF.js +1 -0
  126. package/dist/chunk-PUPM2YGF.js +376 -0
  127. package/dist/chunk-PZXZJBMP.js +620 -0
  128. package/dist/chunk-Q72LJOVE.js +169 -0
  129. package/dist/chunk-QCDXZBC5.js +247 -0
  130. package/dist/chunk-QDYDBCFP.js +197 -0
  131. package/dist/chunk-QJJ6MADV.js +274 -0
  132. package/dist/chunk-QKXCPTFS.js +349 -0
  133. package/dist/chunk-QWCMU5B2.js +860 -0
  134. package/dist/chunk-RGQWWKLA.js +634 -0
  135. package/dist/chunk-RIBDXGUG.js +108 -0
  136. package/dist/chunk-RJQOOHT2.js +214 -0
  137. package/dist/chunk-RNGLIHFA.js +191 -0
  138. package/dist/chunk-RQ3HASCC.js +6 -0
  139. package/dist/chunk-RSOJT5YW.js +124 -0
  140. package/dist/chunk-RTIKSY6A.js +1 -0
  141. package/dist/chunk-RUZHTGKB.js +237 -0
  142. package/dist/chunk-RW6ZL2FQ.js +284 -0
  143. package/dist/chunk-S7F6XLTK.js +540 -0
  144. package/dist/chunk-SBMNWWBV.js +155 -0
  145. package/dist/chunk-SDTQPRRB.js +1 -0
  146. package/dist/chunk-SYNOJOQO.js +377 -0
  147. package/dist/chunk-T753XT36.js +1 -0
  148. package/dist/chunk-TKQYWZ5V.js +196 -0
  149. package/dist/chunk-TKVFVF5D.js +95 -0
  150. package/dist/chunk-TOXAGRDQ.js +144 -0
  151. package/dist/chunk-TUTI5HOZ.js +112 -0
  152. package/dist/chunk-U7TFM2JA.js +140 -0
  153. package/dist/chunk-UC2WGUP3.js +627 -0
  154. package/dist/chunk-UWFLYMAR.js +1126 -0
  155. package/dist/chunk-UWSZB7BJ.js +1 -0
  156. package/dist/chunk-UYG5L34B.js +87 -0
  157. package/dist/chunk-VBOXAEPI.js +336 -0
  158. package/dist/chunk-VCDBXW5H.js +188 -0
  159. package/dist/chunk-VCXPHHDS.js +1 -0
  160. package/dist/chunk-VEAYTRPX.js +414 -0
  161. package/dist/chunk-VFCUIDRT.js +1 -0
  162. package/dist/chunk-VQHPQEUH.js +273 -0
  163. package/dist/chunk-VXDZFJGP.js +874 -0
  164. package/dist/chunk-WS56BL3D.js +1 -0
  165. package/dist/chunk-WSW6ZFAQ.js +98 -0
  166. package/dist/chunk-WVKKJA6U.js +870 -0
  167. package/dist/chunk-XNUVB5OL.js +375 -0
  168. package/dist/chunk-XOSCY2CW.js +242 -0
  169. package/dist/chunk-XXXEFXY5.js +99 -0
  170. package/dist/chunk-Y5CSSPV4.js +1 -0
  171. package/dist/chunk-Y6DU4J47.js +1 -0
  172. package/dist/chunk-YC6WORFL.js +535 -0
  173. package/dist/chunk-YDGVJETP.js +60 -0
  174. package/dist/chunk-YF4NSSMT.js +366 -0
  175. package/dist/chunk-YG3ZJHGU.js +223 -0
  176. package/dist/chunk-YIJ55TBC.js +1 -0
  177. package/dist/chunk-YKZ4RZZF.js +1 -0
  178. package/dist/chunk-YLLCXTMC.js +37 -0
  179. package/dist/chunk-YTA74UZR.js +1 -0
  180. package/dist/chunk-Z7H7VSFZ.js +323 -0
  181. package/dist/chunk-ZK2H4O5P.js +1 -0
  182. package/dist/chunk-ZVFE5QGU.js +524 -0
  183. package/dist/coachmark/index.js +4 -0
  184. package/dist/code-block/index.js +4 -0
  185. package/dist/color-field/index.js +7 -0
  186. package/dist/commerce/index.js +20 -0
  187. package/dist/connectivity/index.js +2 -0
  188. package/dist/context-menu/index.js +5 -0
  189. package/dist/copy-button/index.js +3 -0
  190. package/dist/data-grid/index.js +24 -0
  191. package/dist/date-range-picker/index.js +13 -0
  192. package/dist/description-list/index.js +2 -0
  193. package/dist/dialog/index.js +3 -0
  194. package/dist/empty-state/index.js +3 -0
  195. package/dist/error-summary/index.js +3 -0
  196. package/dist/fab/index.js +2 -0
  197. package/dist/file-upload/index.js +9 -0
  198. package/dist/filter-bar/index.js +8 -0
  199. package/dist/flow-run/index.js +14 -0
  200. package/dist/form/index.js +3 -0
  201. package/dist/gallery/index.js +2 -0
  202. package/dist/header/index.js +5 -0
  203. package/dist/hold-to-confirm/index.js +4 -0
  204. package/dist/index.d.ts +22883 -0
  205. package/dist/index.js +158 -0
  206. package/dist/infinite-list/index.js +2 -0
  207. package/dist/inline-edit/index.js +5 -0
  208. package/dist/input/index.js +5 -0
  209. package/dist/interactions/index.js +2 -0
  210. package/dist/invoice/index.js +10 -0
  211. package/dist/kbd/index.js +1 -0
  212. package/dist/keyboard/index.js +3 -0
  213. package/dist/list-row/index.js +7 -0
  214. package/dist/log-viewer/index.js +2 -0
  215. package/dist/map-view/index.js +3 -0
  216. package/dist/markdown/index.js +6 -0
  217. package/dist/metric/index.js +4 -0
  218. package/dist/money/index.js +3 -0
  219. package/dist/nav-drawer/index.js +2 -0
  220. package/dist/navigation/index.js +2 -0
  221. package/dist/notification-center/index.js +16 -0
  222. package/dist/number-roll/index.js +3 -0
  223. package/dist/one-handed/index.js +5 -0
  224. package/dist/os-surfaces/index.js +3 -0
  225. package/dist/otp/index.js +7 -0
  226. package/dist/page-header/index.js +1 -0
  227. package/dist/payment-method/index.js +12 -0
  228. package/dist/payment-status/index.js +16 -0
  229. package/dist/permission/index.js +2 -0
  230. package/dist/picker/index.js +7 -0
  231. package/dist/progress/index.js +5 -0
  232. package/dist/pull-to-refresh/index.js +4 -0
  233. package/dist/radio-group/index.js +6 -0
  234. package/dist/rating/index.js +2 -0
  235. package/dist/receivables/index.js +20 -0
  236. package/dist/refund/index.js +16 -0
  237. package/dist/save-status/index.js +3 -0
  238. package/dist/scroll-area/index.js +1 -0
  239. package/dist/search-bar/index.js +5 -0
  240. package/dist/segmented-control/index.js +5 -0
  241. package/dist/sheet/index.js +4 -0
  242. package/dist/skeleton/index.js +2 -0
  243. package/dist/slider/index.js +5 -0
  244. package/dist/spinner/index.js +2 -0
  245. package/dist/split-button/index.js +7 -0
  246. package/dist/spotlight/index.js +2 -0
  247. package/dist/stage-timeline/index.js +4 -0
  248. package/dist/status-dot/index.js +2 -0
  249. package/dist/stepper/index.js +6 -0
  250. package/dist/streaming-text/index.js +3 -0
  251. package/dist/survey/index.js +19 -0
  252. package/dist/switch/index.js +5 -0
  253. package/dist/system-state/index.js +4 -0
  254. package/dist/tab-bar/index.js +2 -0
  255. package/dist/tabs/index.js +2 -0
  256. package/dist/textarea/index.js +6 -0
  257. package/dist/thinking/index.js +2 -0
  258. package/dist/timeline/index.js +4 -0
  259. package/dist/toast/index.js +2 -0
  260. package/dist/toolbar/index.js +2 -0
  261. package/dist/tour/index.js +11 -0
  262. package/dist/truncation-notice/index.js +2 -0
  263. package/dist/update-notice/index.js +11 -0
  264. package/dist/video-player/index.js +2 -0
  265. package/dist/viewfinder/index.js +5 -0
  266. package/dist/widget/index.js +4 -0
  267. package/dist/wizard/index.js +6 -0
  268. package/package.json +76 -3
  269. package/README.md +0 -3
@@ -0,0 +1,874 @@
1
+ import { Icon } from './chunk-TKVFVF5D.js';
2
+ import * as React2 from 'react';
3
+ import { StyleSheet, Pressable, Text, useWindowDimensions, Animated, Easing, AccessibilityInfo, Platform, findNodeHandle, BackHandler, PanResponder, View } from 'react-native';
4
+ import { createStyleSheet, useLoamTheme, useStyles } from '@btndprojects/loam-theme-native';
5
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/navigation/geometry.ts
8
+ var NAV_PARALLAX_RATIO = 0.3;
9
+ var NAV_EDGE_HIT_WIDTH = 20;
10
+ var NAV_AXIS_LOCK_SLOP = 8;
11
+ var NAV_SWIPE_COMPLETE_FRACTION = 0.5;
12
+ var NAV_SWIPE_COMPLETE_VELOCITY = 0.5;
13
+ var NAV_TAB_FADE_SCALE = 0.97;
14
+ var NAV_HERO_CONTENT_FADE_START = 0.5;
15
+ var NAV_MODAL_TOP_INSET = 34;
16
+ var NAV_MODAL_HEADER_HEIGHT = 48;
17
+ function clampProgress(progress) {
18
+ if (Number.isNaN(progress)) return 0;
19
+ return Math.max(0, Math.min(1, progress));
20
+ }
21
+ function directionSign(rtl) {
22
+ return rtl ? -1 : 1;
23
+ }
24
+ function incomingTranslateX(progress, width, rtl) {
25
+ return (1 - clampProgress(progress)) * width * directionSign(rtl);
26
+ }
27
+ function parallaxTranslateX(progress, width, rtl) {
28
+ return -clampProgress(progress) * width * NAV_PARALLAX_RATIO * directionSign(rtl);
29
+ }
30
+ function parallaxDimOpacity(progress) {
31
+ return clampProgress(progress);
32
+ }
33
+ function progressFromEdgeSwipe(dx, width, rtl) {
34
+ if (width <= 0) return 1;
35
+ const travelled = dx * directionSign(rtl);
36
+ return 1 - clampProgress(travelled / width);
37
+ }
38
+ function shouldCompletePop(dx, vx, width, rtl) {
39
+ const sign = directionSign(rtl);
40
+ const travelled = dx * sign;
41
+ const velocity = vx * sign;
42
+ if (travelled <= 0) return false;
43
+ return travelled >= width * NAV_SWIPE_COMPLETE_FRACTION || velocity >= NAV_SWIPE_COMPLETE_VELOCITY;
44
+ }
45
+ function claimsHorizontalAxis(dx, dy, slop = NAV_AXIS_LOCK_SLOP) {
46
+ return Math.abs(dx) > slop && Math.abs(dx) > Math.abs(dy);
47
+ }
48
+ function isFromLeadingEdge(x, width, rtl, edgeWidth = NAV_EDGE_HIT_WIDTH) {
49
+ return rtl ? x >= width - edgeWidth : x <= edgeWidth;
50
+ }
51
+ function modalTranslateY(progress, height) {
52
+ return (1 - clampProgress(progress)) * height;
53
+ }
54
+ function tabFadeScale(progress) {
55
+ const p = clampProgress(progress);
56
+ return NAV_TAB_FADE_SCALE + (1 - NAV_TAB_FADE_SCALE) * p;
57
+ }
58
+ function heroContentOpacity(progress) {
59
+ const p = clampProgress(progress);
60
+ if (p <= NAV_HERO_CONTENT_FADE_START) return 0;
61
+ return (p - NAV_HERO_CONTENT_FADE_START) / (1 - NAV_HERO_CONTENT_FADE_START);
62
+ }
63
+ function heroTransform(from, to, progress) {
64
+ const p = clampProgress(progress);
65
+ const scaleX = to.width > 0 ? from.width / to.width : 1;
66
+ const scaleY = to.height > 0 ? from.height / to.height : 1;
67
+ const dx = from.x + from.width / 2 - (to.x + to.width / 2);
68
+ const dy = from.y + from.height / 2 - (to.y + to.height / 2);
69
+ return {
70
+ translateX: dx * (1 - p),
71
+ translateY: dy * (1 - p),
72
+ scaleX: scaleX + (1 - scaleX) * p,
73
+ scaleY: scaleY + (1 - scaleY) * p
74
+ };
75
+ }
76
+ function NavBackButton({
77
+ onPress,
78
+ previousTitle,
79
+ platform: platformProp,
80
+ accessibilityLabel,
81
+ disabled = false
82
+ }) {
83
+ const theme = useLoamTheme();
84
+ const platform = platformProp ?? navPlatform(theme.platform);
85
+ const { styles } = useStyles(sheet, { platform });
86
+ const back = theme.rtl ? "chevron-right" : "chevron-left";
87
+ const arrow = theme.rtl ? "arrow-right" : "arrow-left";
88
+ const showTitle = platform === "iOS" && previousTitle !== void 0;
89
+ const label = accessibilityLabel ?? (previousTitle ? `Back to ${previousTitle}` : "Back");
90
+ return /* @__PURE__ */ jsxs(
91
+ Pressable,
92
+ {
93
+ accessibilityRole: "button",
94
+ accessibilityLabel: label,
95
+ accessibilityState: { disabled },
96
+ disabled,
97
+ onPress: disabled ? void 0 : onPress,
98
+ hitSlop: theme.space.s2,
99
+ style: ({ pressed }) => [
100
+ styles.back,
101
+ disabled ? styles.backDisabled : null,
102
+ pressed && !disabled ? styles.backPressed : null
103
+ ],
104
+ children: [
105
+ /* @__PURE__ */ jsx(
106
+ Icon,
107
+ {
108
+ name: platform === "iOS" ? back : arrow,
109
+ size: theme.icon.md,
110
+ color: theme.color.accentStrong
111
+ }
112
+ ),
113
+ showTitle ? /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles.backLabel, children: previousTitle }) : null
114
+ ]
115
+ }
116
+ );
117
+ }
118
+ function NavBar({
119
+ platform,
120
+ title,
121
+ previousTitle,
122
+ onBack,
123
+ backLabel,
124
+ leading,
125
+ trailing
126
+ }) {
127
+ const { styles } = useStyles(sheet, { platform });
128
+ return /* @__PURE__ */ jsxs(View, { accessibilityRole: "header", style: styles.bar, children: [
129
+ onBack ? /* @__PURE__ */ jsx(
130
+ NavBackButton,
131
+ {
132
+ platform,
133
+ previousTitle,
134
+ accessibilityLabel: previousTitle ? void 0 : backLabel,
135
+ onPress: onBack
136
+ }
137
+ ) : leading,
138
+ /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles.barTitle, children: title }),
139
+ trailing ? /* @__PURE__ */ jsx(View, { style: styles.barTrailing, children: trailing }) : null
140
+ ] });
141
+ }
142
+ function navPlatform(platform) {
143
+ return platform === "Android" ? "Android" : "iOS";
144
+ }
145
+ function NavigationStack({
146
+ screens,
147
+ onPop,
148
+ onExit,
149
+ platform: platformProp,
150
+ swipeEnabled,
151
+ backLabel = "Back",
152
+ announce = true,
153
+ style
154
+ }) {
155
+ const theme = useLoamTheme();
156
+ const platform = platformProp ?? navPlatform(theme.platform);
157
+ const { styles } = useStyles(sheet, { platform });
158
+ const rtl = theme.rtl;
159
+ const reduce = theme.reduceMotion;
160
+ const window = useWindowDimensions();
161
+ const depth = screens.length;
162
+ const top = depth > 0 ? screens[depth - 1] : void 0;
163
+ const below = depth > 1 ? screens[depth - 2] : void 0;
164
+ const [width, setWidth] = React2.useState(window.width);
165
+ const onRootLayout = (e) => {
166
+ const next = e.nativeEvent.layout.width;
167
+ if (next > 0) setWidth(next);
168
+ };
169
+ const progress = React2.useRef(new Animated.Value(1)).current;
170
+ const [exiting, setExiting] = React2.useState(null);
171
+ const over = exiting ?? top;
172
+ const under = exiting ? top : below;
173
+ const page = theme.motion.page;
174
+ const pageEasing = React2.useMemo(() => Easing.bezier(...page.easing), [page.easing]);
175
+ const live = React2.useRef({ top, below, onPop, onExit, width, rtl });
176
+ live.current = { top, below, onPop, onExit, width, rtl };
177
+ const screenRef = React2.useRef(null);
178
+ const announceRef = React2.useRef(null);
179
+ const pendingFocusRef = React2.useRef(null);
180
+ const skipExitRef = React2.useRef(false);
181
+ const historyRef = React2.useRef(null);
182
+ const revealTop = React2.useCallback(
183
+ (screen) => {
184
+ if (announce) {
185
+ const text = screen.announcement ?? screen.title;
186
+ if (announceRef.current !== text) AccessibilityInfo.announceForAccessibility(text);
187
+ announceRef.current = text;
188
+ }
189
+ pendingFocusRef.current = screen.key;
190
+ },
191
+ [announce]
192
+ );
193
+ React2.useEffect(() => {
194
+ const wanted = pendingFocusRef.current;
195
+ if (wanted === null || wanted !== top?.key) return;
196
+ pendingFocusRef.current = null;
197
+ const node = screenRef.current;
198
+ if (!node || Platform.OS === "web") return;
199
+ const tag = findNodeHandle(node);
200
+ if (tag !== null) AccessibilityInfo.setAccessibilityFocus(tag);
201
+ });
202
+ React2.useEffect(() => {
203
+ const previous = historyRef.current;
204
+ if (top) historyRef.current = { depth, key: top.key, screen: top };
205
+ if (!top) return;
206
+ if (!previous) return;
207
+ if (previous.key === top.key) return;
208
+ if (skipExitRef.current) {
209
+ skipExitRef.current = false;
210
+ progress.setValue(1);
211
+ revealTop(top);
212
+ return;
213
+ }
214
+ if (depth < previous.depth) {
215
+ setExiting(previous.screen);
216
+ progress.setValue(1);
217
+ Animated.timing(progress, {
218
+ toValue: 0,
219
+ duration: page.duration,
220
+ easing: pageEasing,
221
+ useNativeDriver: true
222
+ }).start(({ finished }) => {
223
+ if (finished) {
224
+ setExiting(null);
225
+ progress.setValue(1);
226
+ }
227
+ });
228
+ } else {
229
+ progress.setValue(0);
230
+ Animated.timing(progress, {
231
+ toValue: 1,
232
+ duration: page.duration,
233
+ easing: pageEasing,
234
+ useNativeDriver: true
235
+ }).start();
236
+ }
237
+ revealTop(top);
238
+ }, [depth, top?.key]);
239
+ React2.useEffect(() => {
240
+ if (platform !== "Android") return;
241
+ const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
242
+ const { top: current, below: parent, onPop: pop, onExit: exit } = live.current;
243
+ if (!current || !parent) {
244
+ exit?.();
245
+ return false;
246
+ }
247
+ pop?.(current, "system-back");
248
+ return true;
249
+ });
250
+ return () => subscription?.remove();
251
+ }, [platform]);
252
+ const canPop = under !== void 0 && over !== void 0;
253
+ const swipeActive = (swipeEnabled ?? platform === "iOS") && canPop && exiting === null;
254
+ const startXRef = React2.useRef(0);
255
+ const swipeRef = React2.useRef({ active: swipeActive, page, pageEasing });
256
+ swipeRef.current = { active: swipeActive, page, pageEasing };
257
+ const pan = React2.useMemo(
258
+ () => PanResponder.create({
259
+ onStartShouldSetPanResponderCapture: (e) => {
260
+ startXRef.current = e.nativeEvent.locationX;
261
+ return false;
262
+ },
263
+ onMoveShouldSetPanResponder: (_e, g) => {
264
+ if (!swipeRef.current.active) return false;
265
+ if (!claimsHorizontalAxis(g.dx, g.dy)) return false;
266
+ const { width: w, rtl: isRtl } = live.current;
267
+ return isFromLeadingEdge(startXRef.current, w, isRtl);
268
+ },
269
+ onPanResponderMove: (_e, g) => {
270
+ const { width: w, rtl: isRtl } = live.current;
271
+ progress.setValue(progressFromEdgeSwipe(g.dx, w, isRtl));
272
+ },
273
+ onPanResponderRelease: (_e, g) => {
274
+ const { top: current, width: w, rtl: isRtl, onPop: pop } = live.current;
275
+ const { page: spec, pageEasing: easing } = swipeRef.current;
276
+ if (current && shouldCompletePop(g.dx, g.vx, w, isRtl)) {
277
+ Animated.timing(progress, {
278
+ toValue: 0,
279
+ duration: spec.duration,
280
+ easing,
281
+ useNativeDriver: true
282
+ }).start(({ finished }) => {
283
+ if (!finished) return;
284
+ skipExitRef.current = true;
285
+ pop?.(current, "edge-swipe");
286
+ });
287
+ return;
288
+ }
289
+ Animated.spring(progress, { toValue: 1, useNativeDriver: true }).start();
290
+ },
291
+ onPanResponderTerminate: () => {
292
+ Animated.spring(progress, { toValue: 1, useNativeDriver: true }).start();
293
+ }
294
+ }),
295
+ [progress]
296
+ );
297
+ const overTranslateX = React2.useMemo(
298
+ () => progress.interpolate({
299
+ inputRange: [0, 1],
300
+ outputRange: [incomingTranslateX(0, width, rtl), incomingTranslateX(1, width, rtl)]
301
+ }),
302
+ [progress, width, rtl]
303
+ );
304
+ const underTranslateX = React2.useMemo(
305
+ () => progress.interpolate({
306
+ inputRange: [0, 1],
307
+ outputRange: [parallaxTranslateX(0, width, rtl), parallaxTranslateX(1, width, rtl)]
308
+ }),
309
+ [progress, width, rtl]
310
+ );
311
+ const dimOpacity = React2.useMemo(
312
+ () => progress.interpolate({
313
+ inputRange: [0, 1],
314
+ outputRange: [parallaxDimOpacity(0), parallaxDimOpacity(1)]
315
+ }),
316
+ [progress]
317
+ );
318
+ const overStyle = reduce ? { opacity: progress } : { transform: [{ translateX: overTranslateX }] };
319
+ const underStyle = reduce ? {} : { transform: [{ translateX: underTranslateX }] };
320
+ if (!over) return /* @__PURE__ */ jsx(View, { style: [styles.root, style] });
321
+ const rootScreen = screens[0];
322
+ const overIsRoot = over === rootScreen;
323
+ const underIsRoot = under !== void 0 && under === rootScreen;
324
+ return /* @__PURE__ */ jsxs(View, { style: [styles.root, style], onLayout: onRootLayout, children: [
325
+ under ? /* @__PURE__ */ jsxs(
326
+ Animated.View,
327
+ {
328
+ ref: exiting ? screenRef : void 0,
329
+ role: "group",
330
+ accessibilityLabel: under.title,
331
+ style: [styles.screen, underIsRoot ? styles.screenRoot : styles.screenPushed, underStyle],
332
+ children: [
333
+ under.hideBar ? null : /* @__PURE__ */ jsx(
334
+ NavBar,
335
+ {
336
+ platform,
337
+ title: under.title,
338
+ leading: under.leading,
339
+ trailing: under.trailing,
340
+ backLabel
341
+ }
342
+ ),
343
+ /* @__PURE__ */ jsx(View, { style: styles.body, children: under.content }),
344
+ reduce ? null : /* @__PURE__ */ jsx(
345
+ Animated.View,
346
+ {
347
+ pointerEvents: "none",
348
+ style: [StyleSheet.absoluteFill, styles.dim, { opacity: dimOpacity }]
349
+ }
350
+ )
351
+ ]
352
+ },
353
+ under.key
354
+ ) : null,
355
+ /* @__PURE__ */ jsxs(
356
+ Animated.View,
357
+ {
358
+ ref: exiting ? void 0 : screenRef,
359
+ role: "group",
360
+ accessibilityLabel: over.title,
361
+ pointerEvents: exiting ? "none" : "auto",
362
+ style: [styles.screen, overIsRoot ? styles.screenRoot : styles.screenPushed, overStyle],
363
+ ...swipeActive ? pan.panHandlers : {},
364
+ children: [
365
+ over.hideBar ? null : /* @__PURE__ */ jsx(
366
+ NavBar,
367
+ {
368
+ platform,
369
+ title: over.title,
370
+ previousTitle: under?.title,
371
+ onBack: canPop ? () => {
372
+ const { top: current, onPop: pop } = live.current;
373
+ if (current) pop?.(current, "back-button");
374
+ } : void 0,
375
+ leading: over.leading,
376
+ trailing: over.trailing,
377
+ backLabel
378
+ }
379
+ ),
380
+ /* @__PURE__ */ jsx(View, { style: styles.body, children: over.content })
381
+ ]
382
+ },
383
+ over.key
384
+ )
385
+ ] });
386
+ }
387
+ var sheet = createStyleSheet((theme) => ({
388
+ root: {
389
+ flex: 1,
390
+ overflow: "hidden",
391
+ backgroundColor: theme.color.bg
392
+ },
393
+ screen: {
394
+ ...StyleSheet.absoluteFillObject,
395
+ flexDirection: "column"
396
+ },
397
+ // The base screen sits on the canvas; a pushed one is a `surface` card with
398
+ // `shadow.lg` (the design reference's `box-shadow: var(--sh-lg)`), which also
399
+ // carries `elevation` for Android's depth model.
400
+ screenRoot: {
401
+ backgroundColor: theme.color.bg
402
+ },
403
+ // loam-contrast-ignore — a pushed screen is full-bleed and its parent is
404
+ // dimmed by `scrimLight` (which high contrast leaves alone), so the shadow is
405
+ // depth during the transition, never the thing that draws the screen.
406
+ screenPushed: {
407
+ backgroundColor: theme.color.surface,
408
+ ...theme.shadow.lg
409
+ },
410
+ // The wash over the parallaxing parent. `scrimLight` already carries the
411
+ // alpha, so the animation only scales it from 0 to 1.
412
+ dim: {
413
+ backgroundColor: theme.color.scrimLight
414
+ },
415
+ body: {
416
+ flex: 1
417
+ },
418
+ // Matches `Header`'s metrics: `layout.appbarH` is the only height in the
419
+ // system that branches on platform (44 on iOS, 64 on Android).
420
+ bar: {
421
+ flexDirection: "row",
422
+ alignItems: "center",
423
+ height: theme.layout.appbarH,
424
+ paddingHorizontal: theme.space.s3,
425
+ gap: theme.space.s2,
426
+ backgroundColor: theme.color.surface,
427
+ borderBottomWidth: theme.borderWidth.base,
428
+ borderBottomColor: theme.color.border
429
+ },
430
+ // Headline is the nav-bar title role on both ramps (HIG headline / M3 title).
431
+ // Android indents it, because its arrow carries no label to space it away
432
+ // from — the reference's `margin-inline-start: 8px`.
433
+ barTitle: {
434
+ flex: 1,
435
+ fontFamily: theme.font.sans,
436
+ fontWeight: theme.fontWeight.bold,
437
+ fontSize: theme.type.headline.fontSize,
438
+ lineHeight: theme.type.headline.lineHeight,
439
+ letterSpacing: theme.type.headline.letterSpacing,
440
+ color: theme.color.text,
441
+ variants: {
442
+ platform: {
443
+ iOS: {},
444
+ Android: { marginStart: theme.space.s2 }
445
+ }
446
+ }
447
+ },
448
+ barTrailing: {
449
+ flexDirection: "row",
450
+ alignItems: "center",
451
+ gap: theme.space.s1
452
+ },
453
+ back: {
454
+ flexDirection: "row",
455
+ alignItems: "center",
456
+ gap: theme.space.s1,
457
+ minHeight: theme.tapMin,
458
+ paddingEnd: theme.space.s1,
459
+ borderRadius: theme.radius.full,
460
+ variants: {
461
+ platform: {
462
+ // The chevron sits tight against its label; a bare arrow gets a square
463
+ // touch target instead.
464
+ iOS: { minWidth: theme.space.s7 },
465
+ Android: { minWidth: theme.tapMin, justifyContent: "center" }
466
+ }
467
+ }
468
+ },
469
+ backPressed: {
470
+ backgroundColor: theme.state.pressed
471
+ },
472
+ backDisabled: {
473
+ opacity: theme.state.disabledOpacity
474
+ },
475
+ backLabel: {
476
+ fontFamily: theme.font.sans,
477
+ fontWeight: theme.fontWeight.semibold,
478
+ fontSize: theme.type.body.fontSize,
479
+ lineHeight: theme.type.body.lineHeight,
480
+ letterSpacing: theme.type.body.letterSpacing,
481
+ color: theme.color.accentStrong,
482
+ variants: {
483
+ platform: {
484
+ iOS: {},
485
+ Android: { display: "none" }
486
+ }
487
+ }
488
+ }
489
+ }));
490
+ var FALLBACK_PANEL_HEIGHT = 640;
491
+ function NavModal({
492
+ open,
493
+ title,
494
+ onDismiss,
495
+ dismissLabel = "Cancel",
496
+ confirmLabel,
497
+ onConfirm,
498
+ children,
499
+ announcement,
500
+ scrimLabel = "Dismiss"
501
+ }) {
502
+ const theme = useLoamTheme();
503
+ const { styles } = useStyles(sheet2);
504
+ const [mounted, setMounted] = React2.useState(open);
505
+ const progress = React2.useRef(new Animated.Value(0)).current;
506
+ const heightRef = React2.useRef(FALLBACK_PANEL_HEIGHT);
507
+ const enter = theme.motion.enter;
508
+ const exit = theme.motion.exit;
509
+ const enterEasing = React2.useMemo(() => Easing.bezier(...enter.easing), [enter.easing]);
510
+ const exitEasing = React2.useMemo(() => Easing.bezier(...exit.easing), [exit.easing]);
511
+ React2.useEffect(() => {
512
+ if (open) {
513
+ setMounted(true);
514
+ AccessibilityInfo.announceForAccessibility(announcement ?? title);
515
+ return;
516
+ }
517
+ if (!mounted) return;
518
+ Animated.timing(progress, {
519
+ toValue: 0,
520
+ duration: exit.duration,
521
+ easing: exitEasing,
522
+ useNativeDriver: true
523
+ }).start(({ finished }) => {
524
+ if (finished) setMounted(false);
525
+ });
526
+ }, [open]);
527
+ React2.useEffect(() => {
528
+ if (!mounted || !open) return;
529
+ progress.setValue(0);
530
+ Animated.timing(progress, {
531
+ toValue: 1,
532
+ duration: enter.duration,
533
+ easing: enterEasing,
534
+ useNativeDriver: true
535
+ }).start();
536
+ }, [mounted]);
537
+ const [panelHeight, setPanelHeight] = React2.useState(FALLBACK_PANEL_HEIGHT);
538
+ const onPanelLayout = (e) => {
539
+ const next = e.nativeEvent.layout.height;
540
+ if (next > 0 && next !== heightRef.current) {
541
+ heightRef.current = next;
542
+ setPanelHeight(next);
543
+ }
544
+ };
545
+ const translateY = React2.useMemo(
546
+ () => progress.interpolate({
547
+ inputRange: [0, 1],
548
+ outputRange: [modalTranslateY(0, panelHeight), modalTranslateY(1, panelHeight)]
549
+ }),
550
+ [progress, panelHeight]
551
+ );
552
+ if (!mounted) return /* @__PURE__ */ jsx(Fragment, {});
553
+ return /* @__PURE__ */ jsxs(View, { style: styles.overlayRoot, pointerEvents: "box-none", children: [
554
+ /* @__PURE__ */ jsx(Animated.View, { style: [StyleSheet.absoluteFill, styles.scrim, { opacity: progress }], children: /* @__PURE__ */ jsx(
555
+ Pressable,
556
+ {
557
+ style: StyleSheet.absoluteFill,
558
+ accessibilityRole: "button",
559
+ accessibilityLabel: scrimLabel,
560
+ onPress: onDismiss
561
+ }
562
+ ) }),
563
+ /* @__PURE__ */ jsxs(
564
+ Animated.View,
565
+ {
566
+ role: "dialog",
567
+ accessibilityViewIsModal: true,
568
+ accessibilityLabel: title,
569
+ onLayout: onPanelLayout,
570
+ style: [styles.panel, { transform: [{ translateY }] }],
571
+ children: [
572
+ /* @__PURE__ */ jsxs(View, { style: styles.modalBar, children: [
573
+ /* @__PURE__ */ jsx(
574
+ Pressable,
575
+ {
576
+ accessibilityRole: "button",
577
+ accessibilityLabel: dismissLabel,
578
+ onPress: onDismiss,
579
+ style: ({ pressed }) => [
580
+ styles.modalAction,
581
+ pressed ? styles.modalActionPressed : null
582
+ ],
583
+ children: /* @__PURE__ */ jsx(Text, { style: styles.modalDismiss, children: dismissLabel })
584
+ }
585
+ ),
586
+ /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles.modalTitle, children: title }),
587
+ confirmLabel ? /* @__PURE__ */ jsx(
588
+ Pressable,
589
+ {
590
+ accessibilityRole: "button",
591
+ accessibilityLabel: confirmLabel,
592
+ onPress: onConfirm,
593
+ style: ({ pressed }) => [
594
+ styles.modalAction,
595
+ pressed ? styles.modalActionPressed : null
596
+ ],
597
+ children: /* @__PURE__ */ jsx(Text, { style: styles.modalConfirm, children: confirmLabel })
598
+ }
599
+ ) : /* @__PURE__ */ jsx(View, { style: styles.modalAction })
600
+ ] }),
601
+ /* @__PURE__ */ jsx(View, { style: styles.modalBody, children })
602
+ ]
603
+ }
604
+ )
605
+ ] });
606
+ }
607
+ function NavTabView({
608
+ value,
609
+ tabs,
610
+ announce = true,
611
+ style
612
+ }) {
613
+ const theme = useLoamTheme();
614
+ const { styles } = useStyles(sheet2);
615
+ const page = theme.motion.page;
616
+ const pageEasing = React2.useMemo(() => Easing.bezier(...page.easing), [page.easing]);
617
+ const active = tabs.find((tab) => tab.value === value);
618
+ const [outgoing, setOutgoing] = React2.useState(null);
619
+ const previousRef = React2.useRef(active);
620
+ const visited = React2.useRef(new Set(active ? [active.value] : []));
621
+ if (active) visited.current.add(active.value);
622
+ const panes = tabs.filter((tab) => visited.current.has(tab.value));
623
+ const fade = React2.useRef(new Animated.Value(1)).current;
624
+ React2.useEffect(() => {
625
+ const previous = previousRef.current;
626
+ previousRef.current = active;
627
+ if (!active || !previous || previous.value === active.value) return;
628
+ if (announce) AccessibilityInfo.announceForAccessibility(active.announcement ?? active.title);
629
+ setOutgoing(previous);
630
+ fade.setValue(0);
631
+ Animated.timing(fade, {
632
+ toValue: 1,
633
+ duration: page.duration,
634
+ easing: pageEasing,
635
+ useNativeDriver: true
636
+ }).start(({ finished }) => {
637
+ if (finished) setOutgoing(null);
638
+ });
639
+ }, [value]);
640
+ const incomingScale = fade.interpolate({
641
+ inputRange: [0, 1],
642
+ outputRange: [NAV_TAB_FADE_SCALE, 1]
643
+ });
644
+ const outgoingOpacity = fade.interpolate({ inputRange: [0, 1], outputRange: [1, 0] });
645
+ const outgoingScale = fade.interpolate({
646
+ inputRange: [0, 1],
647
+ outputRange: [1, NAV_TAB_FADE_SCALE]
648
+ });
649
+ return /* @__PURE__ */ jsx(View, { style: [styles.tabRoot, style], children: panes.map((pane) => {
650
+ const isActive = pane.value === value;
651
+ const isOutgoing = outgoing?.value === pane.value;
652
+ return /* @__PURE__ */ jsx(
653
+ Animated.View,
654
+ {
655
+ role: "group",
656
+ accessibilityLabel: pane.title,
657
+ importantForAccessibility: isActive ? "auto" : "no-hide-descendants",
658
+ pointerEvents: isActive ? "auto" : "none",
659
+ style: [
660
+ styles.tabPane,
661
+ isActive ? { opacity: fade, transform: [{ scale: incomingScale }] } : isOutgoing ? { opacity: outgoingOpacity, transform: [{ scale: outgoingScale }] } : styles.tabPaneHidden
662
+ ],
663
+ children: pane.content
664
+ },
665
+ pane.value
666
+ );
667
+ }) });
668
+ }
669
+ function NavHero({
670
+ active,
671
+ from,
672
+ to,
673
+ children,
674
+ onMorphEnd,
675
+ accessibilityLabel,
676
+ announcement
677
+ }) {
678
+ const theme = useLoamTheme();
679
+ const { styles } = useStyles(sheet2);
680
+ const reduce = theme.reduceMotion;
681
+ const hero = theme.motion.hero;
682
+ const heroEasing = React2.useMemo(() => Easing.bezier(...hero.easing), [hero.easing]);
683
+ const [mounted, setMounted] = React2.useState(active);
684
+ const progress = React2.useRef(new Animated.Value(0)).current;
685
+ const [bounds, setBounds] = React2.useState(null);
686
+ const onRootLayout = (e) => {
687
+ const { width, height } = e.nativeEvent.layout;
688
+ if (width > 0 && height > 0) setBounds({ x: 0, y: 0, width, height });
689
+ };
690
+ const endRef = React2.useRef(onMorphEnd);
691
+ endRef.current = onMorphEnd;
692
+ React2.useEffect(() => {
693
+ if (active) {
694
+ setMounted(true);
695
+ if (announcement ?? accessibilityLabel) {
696
+ AccessibilityInfo.announceForAccessibility(announcement ?? accessibilityLabel);
697
+ }
698
+ return;
699
+ }
700
+ if (!mounted) return;
701
+ Animated.timing(progress, {
702
+ toValue: 0,
703
+ duration: hero.duration,
704
+ easing: heroEasing,
705
+ useNativeDriver: true
706
+ }).start(({ finished }) => {
707
+ if (!finished) return;
708
+ setMounted(false);
709
+ endRef.current?.(false);
710
+ });
711
+ }, [active]);
712
+ React2.useEffect(() => {
713
+ if (!mounted || !active) return;
714
+ progress.setValue(0);
715
+ Animated.timing(progress, {
716
+ toValue: 1,
717
+ duration: hero.duration,
718
+ easing: heroEasing,
719
+ useNativeDriver: true
720
+ }).start(({ finished }) => {
721
+ if (finished) endRef.current?.(true);
722
+ });
723
+ }, [mounted]);
724
+ const target = to ?? bounds;
725
+ const start = target ? heroTransform(from, target, 0) : null;
726
+ const morphStyle = reduce || !start ? { opacity: progress } : {
727
+ transform: [
728
+ {
729
+ translateX: progress.interpolate({
730
+ inputRange: [0, 1],
731
+ outputRange: [start.translateX, 0]
732
+ })
733
+ },
734
+ {
735
+ translateY: progress.interpolate({
736
+ inputRange: [0, 1],
737
+ outputRange: [start.translateY, 0]
738
+ })
739
+ },
740
+ {
741
+ scaleX: progress.interpolate({ inputRange: [0, 1], outputRange: [start.scaleX, 1] })
742
+ },
743
+ {
744
+ scaleY: progress.interpolate({ inputRange: [0, 1], outputRange: [start.scaleY, 1] })
745
+ }
746
+ ]
747
+ };
748
+ const contentOpacity = reduce ? progress : progress.interpolate({
749
+ inputRange: [NAV_HERO_CONTENT_FADE_START, 1],
750
+ outputRange: [0, 1],
751
+ extrapolate: "clamp"
752
+ });
753
+ const frame = to ? { position: "absolute", top: to.y, start: to.x, width: to.width, height: to.height } : StyleSheet.absoluteFillObject;
754
+ return /* @__PURE__ */ jsx(
755
+ View,
756
+ {
757
+ style: styles.overlayRoot,
758
+ pointerEvents: mounted ? "auto" : "none",
759
+ onLayout: onRootLayout,
760
+ children: mounted ? /* @__PURE__ */ jsx(
761
+ Animated.View,
762
+ {
763
+ role: "group",
764
+ accessibilityLabel,
765
+ style: [styles.heroSurface, frame, morphStyle],
766
+ children: /* @__PURE__ */ jsx(Animated.View, { style: [styles.heroContent, { opacity: contentOpacity }], children })
767
+ }
768
+ ) : null
769
+ }
770
+ );
771
+ }
772
+ var sheet2 = createStyleSheet((theme) => ({
773
+ // Both the modal and the hero layer float over whatever container they are
774
+ // placed in, rather than taking part in its flow.
775
+ overlayRoot: {
776
+ ...StyleSheet.absoluteFillObject,
777
+ zIndex: theme.z.overlay
778
+ },
779
+ scrim: {
780
+ backgroundColor: theme.color.scrim
781
+ },
782
+ // `NAV_MODAL_TOP_INSET` (34px) is the design reference's literal: the sliver of
783
+ // the covered screen that keeps the presentation reading as "over".
784
+ // loam-contrast-ignore — the boundary here is the scrim, not the shadow.
785
+ // `color.scrim` is a 44% wash and is *not* re-resolved by high contrast, so
786
+ // this surface is still separated from the page when the elevation zeroes
787
+ // out. A border would put a hairline around every modal for no gain.
788
+ panel: {
789
+ position: "absolute",
790
+ top: NAV_MODAL_TOP_INSET,
791
+ start: 0,
792
+ end: 0,
793
+ bottom: 0,
794
+ backgroundColor: theme.color.surface,
795
+ borderTopStartRadius: theme.radius["2xl"],
796
+ borderTopEndRadius: theme.radius["2xl"],
797
+ overflow: "hidden",
798
+ zIndex: theme.z.modal,
799
+ ...theme.shadow.lg
800
+ },
801
+ modalBar: {
802
+ flexDirection: "row",
803
+ alignItems: "center",
804
+ justifyContent: "space-between",
805
+ height: NAV_MODAL_HEADER_HEIGHT,
806
+ paddingHorizontal: theme.space.s3,
807
+ gap: theme.space.s2,
808
+ borderBottomWidth: theme.borderWidth.base,
809
+ borderBottomColor: theme.color.border
810
+ },
811
+ modalAction: {
812
+ minHeight: theme.tapMin,
813
+ minWidth: theme.space.s7,
814
+ justifyContent: "center",
815
+ borderRadius: theme.radius.full,
816
+ paddingHorizontal: theme.space.s1
817
+ },
818
+ modalActionPressed: {
819
+ backgroundColor: theme.state.pressed
820
+ },
821
+ // The dismiss is quieter than the confirm on purpose — it is always available,
822
+ // so it does not need to compete.
823
+ modalDismiss: {
824
+ fontFamily: theme.font.sans,
825
+ fontWeight: theme.fontWeight.semibold,
826
+ fontSize: theme.type.body.fontSize,
827
+ lineHeight: theme.type.body.lineHeight,
828
+ color: theme.color.text2
829
+ },
830
+ modalConfirm: {
831
+ fontFamily: theme.font.sans,
832
+ fontWeight: theme.fontWeight.bold,
833
+ fontSize: theme.type.body.fontSize,
834
+ lineHeight: theme.type.body.lineHeight,
835
+ color: theme.color.accentStrong
836
+ },
837
+ modalTitle: {
838
+ flex: 1,
839
+ textAlign: "center",
840
+ fontFamily: theme.font.sans,
841
+ fontWeight: theme.fontWeight.bold,
842
+ fontSize: theme.type.headline.fontSize,
843
+ lineHeight: theme.type.headline.lineHeight,
844
+ letterSpacing: theme.type.headline.letterSpacing,
845
+ color: theme.color.text
846
+ },
847
+ modalBody: {
848
+ flex: 1,
849
+ padding: theme.layout.screenPad
850
+ },
851
+ tabRoot: {
852
+ flex: 1,
853
+ overflow: "hidden"
854
+ },
855
+ // Panes overlap so they can cross-fade; the inactive ones are taken out of
856
+ // layout (and out of the accessibility tree) rather than stacked invisibly.
857
+ tabPane: {
858
+ ...StyleSheet.absoluteFillObject
859
+ },
860
+ tabPaneHidden: {
861
+ display: "none"
862
+ },
863
+ heroSurface: {
864
+ backgroundColor: theme.color.surface,
865
+ borderRadius: theme.radius.md,
866
+ overflow: "hidden",
867
+ zIndex: theme.z.raised
868
+ },
869
+ heroContent: {
870
+ flex: 1
871
+ }
872
+ }));
873
+
874
+ export { NAV_AXIS_LOCK_SLOP, NAV_EDGE_HIT_WIDTH, NAV_HERO_CONTENT_FADE_START, NAV_MODAL_HEADER_HEIGHT, NAV_MODAL_TOP_INSET, NAV_PARALLAX_RATIO, NAV_SWIPE_COMPLETE_FRACTION, NAV_SWIPE_COMPLETE_VELOCITY, NAV_TAB_FADE_SCALE, NavBackButton, NavHero, NavModal, NavTabView, NavigationStack, claimsHorizontalAxis, clampProgress, directionSign, heroContentOpacity, heroTransform, incomingTranslateX, isFromLeadingEdge, modalTranslateY, parallaxDimOpacity, parallaxTranslateX, progressFromEdgeSwipe, shouldCompletePop, tabFadeScale };