@oxyhq/bloom 0.79.0 → 0.80.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 (147) hide show
  1. package/README.md +3 -3
  2. package/lib/commonjs/provider/index.js +11 -7
  3. package/lib/commonjs/provider/index.js.map +1 -1
  4. package/lib/commonjs/scroll/context.js +96 -0
  5. package/lib/commonjs/scroll/context.js.map +1 -0
  6. package/lib/commonjs/scroll/expo-router/index.js +133 -0
  7. package/lib/commonjs/scroll/expo-router/index.js.map +1 -0
  8. package/lib/commonjs/scroll/index.js +85 -21
  9. package/lib/commonjs/scroll/index.js.map +1 -1
  10. package/lib/commonjs/scroll/index.web.js +221 -121
  11. package/lib/commonjs/scroll/index.web.js.map +1 -1
  12. package/lib/commonjs/scroll/scrollable.native.js +58 -0
  13. package/lib/commonjs/scroll/scrollable.native.js.map +1 -0
  14. package/lib/commonjs/scroll/scrollable.web.js +9 -3
  15. package/lib/commonjs/scroll/scrollable.web.js.map +1 -1
  16. package/lib/commonjs/scroll/store.js +47 -25
  17. package/lib/commonjs/scroll/store.js.map +1 -1
  18. package/lib/commonjs/tabs/Tabs.js +263 -63
  19. package/lib/commonjs/tabs/Tabs.js.map +1 -1
  20. package/lib/commonjs/tabs/expo-router/RouterTabs.js +215 -0
  21. package/lib/commonjs/tabs/expo-router/RouterTabs.js.map +1 -0
  22. package/lib/commonjs/tabs/expo-router/index.js +13 -0
  23. package/lib/commonjs/tabs/expo-router/index.js.map +1 -0
  24. package/lib/commonjs/theme/color-policy.js +11 -10
  25. package/lib/commonjs/theme/color-policy.js.map +1 -1
  26. package/lib/module/provider/index.js +10 -6
  27. package/lib/module/provider/index.js.map +1 -1
  28. package/lib/module/scroll/context.js +90 -0
  29. package/lib/module/scroll/context.js.map +1 -0
  30. package/lib/module/scroll/expo-router/index.js +128 -0
  31. package/lib/module/scroll/expo-router/index.js.map +1 -0
  32. package/lib/module/scroll/index.js +80 -21
  33. package/lib/module/scroll/index.js.map +1 -1
  34. package/lib/module/scroll/index.web.js +218 -122
  35. package/lib/module/scroll/index.web.js.map +1 -1
  36. package/lib/module/scroll/scrollable.native.js +54 -0
  37. package/lib/module/scroll/scrollable.native.js.map +1 -0
  38. package/lib/module/scroll/scrollable.web.js +9 -3
  39. package/lib/module/scroll/scrollable.web.js.map +1 -1
  40. package/lib/module/scroll/store.js +47 -25
  41. package/lib/module/scroll/store.js.map +1 -1
  42. package/lib/module/tabs/Tabs.js +271 -65
  43. package/lib/module/tabs/Tabs.js.map +1 -1
  44. package/lib/module/tabs/expo-router/RouterTabs.js +210 -0
  45. package/lib/module/tabs/expo-router/RouterTabs.js.map +1 -0
  46. package/lib/module/tabs/expo-router/index.js +10 -0
  47. package/lib/module/tabs/expo-router/index.js.map +1 -0
  48. package/lib/module/theme/color-policy.js +11 -10
  49. package/lib/module/theme/color-policy.js.map +1 -1
  50. package/lib/typescript/commonjs/provider/index.d.ts +7 -5
  51. package/lib/typescript/commonjs/provider/index.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/scroll/context.d.ts +14 -0
  53. package/lib/typescript/commonjs/scroll/context.d.ts.map +1 -0
  54. package/lib/typescript/commonjs/scroll/expo-router/index.d.ts +7 -0
  55. package/lib/typescript/commonjs/scroll/expo-router/index.d.ts.map +1 -0
  56. package/lib/typescript/commonjs/scroll/index.d.ts +14 -23
  57. package/lib/typescript/commonjs/scroll/index.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/scroll/index.web.d.ts +17 -19
  59. package/lib/typescript/commonjs/scroll/index.web.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/scroll/scrollable.native.d.ts +23 -0
  61. package/lib/typescript/commonjs/scroll/scrollable.native.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts +18 -5
  63. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/scroll/store.d.ts +41 -22
  65. package/lib/typescript/commonjs/scroll/store.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/scroll/types.d.ts +82 -12
  67. package/lib/typescript/commonjs/scroll/types.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/tabs/Tabs.d.ts +41 -1
  69. package/lib/typescript/commonjs/tabs/Tabs.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/tabs/expo-router/RouterTabs.d.ts +80 -0
  71. package/lib/typescript/commonjs/tabs/expo-router/RouterTabs.d.ts.map +1 -0
  72. package/lib/typescript/commonjs/tabs/expo-router/index.d.ts +9 -0
  73. package/lib/typescript/commonjs/tabs/expo-router/index.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/tabs/types.d.ts +48 -4
  75. package/lib/typescript/commonjs/tabs/types.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -1
  77. package/lib/typescript/module/provider/index.d.ts +7 -5
  78. package/lib/typescript/module/provider/index.d.ts.map +1 -1
  79. package/lib/typescript/module/scroll/context.d.ts +14 -0
  80. package/lib/typescript/module/scroll/context.d.ts.map +1 -0
  81. package/lib/typescript/module/scroll/expo-router/index.d.ts +7 -0
  82. package/lib/typescript/module/scroll/expo-router/index.d.ts.map +1 -0
  83. package/lib/typescript/module/scroll/index.d.ts +14 -23
  84. package/lib/typescript/module/scroll/index.d.ts.map +1 -1
  85. package/lib/typescript/module/scroll/index.web.d.ts +17 -19
  86. package/lib/typescript/module/scroll/index.web.d.ts.map +1 -1
  87. package/lib/typescript/module/scroll/scrollable.native.d.ts +23 -0
  88. package/lib/typescript/module/scroll/scrollable.native.d.ts.map +1 -0
  89. package/lib/typescript/module/scroll/scrollable.web.d.ts +18 -5
  90. package/lib/typescript/module/scroll/scrollable.web.d.ts.map +1 -1
  91. package/lib/typescript/module/scroll/store.d.ts +41 -22
  92. package/lib/typescript/module/scroll/store.d.ts.map +1 -1
  93. package/lib/typescript/module/scroll/types.d.ts +82 -12
  94. package/lib/typescript/module/scroll/types.d.ts.map +1 -1
  95. package/lib/typescript/module/tabs/Tabs.d.ts +41 -1
  96. package/lib/typescript/module/tabs/Tabs.d.ts.map +1 -1
  97. package/lib/typescript/module/tabs/expo-router/RouterTabs.d.ts +80 -0
  98. package/lib/typescript/module/tabs/expo-router/RouterTabs.d.ts.map +1 -0
  99. package/lib/typescript/module/tabs/expo-router/index.d.ts +9 -0
  100. package/lib/typescript/module/tabs/expo-router/index.d.ts.map +1 -0
  101. package/lib/typescript/module/tabs/types.d.ts +48 -4
  102. package/lib/typescript/module/tabs/types.d.ts.map +1 -1
  103. package/lib/typescript/module/theme/color-policy.d.ts.map +1 -1
  104. package/package.json +29 -1
  105. package/src/__tests__/BloomProvider.web.test.tsx +8 -15
  106. package/src/__tests__/Tabs.test.tsx +187 -0
  107. package/src/__tests__/optional-peer-imports.test.ts +7 -2
  108. package/src/__tests__/scroll-expo-router-adapter.test.tsx +164 -0
  109. package/src/__tests__/scroll-native.test.tsx +465 -0
  110. package/src/__tests__/scroll-store.test.ts +41 -17
  111. package/src/__tests__/scroll-web.test.tsx +797 -108
  112. package/src/provider/index.tsx +10 -7
  113. package/src/scroll/context.tsx +107 -0
  114. package/src/scroll/expo-router/index.ts +140 -0
  115. package/src/scroll/index.ts +92 -25
  116. package/src/scroll/index.web.tsx +255 -162
  117. package/src/scroll/scrollable.native.ts +64 -0
  118. package/src/scroll/scrollable.web.ts +30 -8
  119. package/src/scroll/store.ts +47 -25
  120. package/src/scroll/types.ts +82 -12
  121. package/src/tabs/Tabs.tsx +352 -77
  122. package/src/tabs/expo-router/RouterTabs.tsx +268 -0
  123. package/src/tabs/expo-router/index.ts +8 -0
  124. package/src/tabs/types.ts +48 -4
  125. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +5 -5
  126. package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +11 -11
  127. package/src/theme/__tests__/policy-legibility.test.ts +10 -7
  128. package/src/theme/color-policy.ts +11 -10
  129. package/lib/commonjs/provider/scroll-provider.js +0 -13
  130. package/lib/commonjs/provider/scroll-provider.js.map +0 -1
  131. package/lib/commonjs/provider/scroll-provider.web.js +0 -13
  132. package/lib/commonjs/provider/scroll-provider.web.js.map +0 -1
  133. package/lib/module/provider/scroll-provider.js +0 -16
  134. package/lib/module/provider/scroll-provider.js.map +0 -1
  135. package/lib/module/provider/scroll-provider.web.js +0 -5
  136. package/lib/module/provider/scroll-provider.web.js.map +0 -1
  137. package/lib/typescript/commonjs/provider/scroll-provider.d.ts +0 -14
  138. package/lib/typescript/commonjs/provider/scroll-provider.d.ts.map +0 -1
  139. package/lib/typescript/commonjs/provider/scroll-provider.web.d.ts +0 -3
  140. package/lib/typescript/commonjs/provider/scroll-provider.web.d.ts.map +0 -1
  141. package/lib/typescript/module/provider/scroll-provider.d.ts +0 -14
  142. package/lib/typescript/module/provider/scroll-provider.d.ts.map +0 -1
  143. package/lib/typescript/module/provider/scroll-provider.web.d.ts +0 -3
  144. package/lib/typescript/module/provider/scroll-provider.web.d.ts.map +0 -1
  145. package/src/__tests__/scroll-native.test.ts +0 -25
  146. package/src/provider/scroll-provider.ts +0 -13
  147. package/src/provider/scroll-provider.web.ts +0 -2
@@ -1,34 +1,93 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * Native variant of the scroll-restoration primitive — a deliberate no-op.
4
+ * Native variant of the scroll-restoration primitive.
5
5
  *
6
- * React Navigation's native-stack keeps every screen mounted while it is in the
7
- * stack, so a list's scroll position survives a push/pop for free. There is
8
- * nothing to save or restore. We still ship the full API surface (provider +
9
- * hook) so consumers write one set of call sites that compile and run on every
10
- * platform; on native the provider just renders its children and the hook does
11
- * nothing.
6
+ * React Navigation's native-stack keeps every screen MOUNTED while it is in the
7
+ * stack, so a list's scroll position survives a push/pop for free this file
8
+ * must not interfere with that, and does not: nothing here runs on focus.
9
+ *
10
+ * What native does not survive is a KEY CHANGE: an in-screen tab or folder swap
11
+ * that re-keys a list, and a screen that genuinely unmounts and remounts. The
12
+ * effect below is therefore keyed on the storage key (plus `enabled`) rather
13
+ * than on focus, which draws exactly that line with no bookkeeping: React
14
+ * re-runs it when the key changes and leaves it alone when a push/pop merely
15
+ * re-focuses the same screen showing the same content.
12
16
  *
13
17
  * Web bundlers select `./index.web` via the `"browser"` export condition in
14
18
  * `package.json`; native bundlers fall through to this file.
15
19
  */
16
-
20
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
21
+ import { useScrollRestorationContext } from "./context.js";
22
+ import { createScroller } from "./scrollable.native.js";
23
+ import { deriveScrollKey } from "./store.js";
24
+ export { ScrollRestorationProvider } from "./context.js";
17
25
  /**
18
- * No-op provider. Renders children unchanged native scroll persistence is
19
- * handled by the navigator, so no per-route state is kept.
26
+ * Preserve and restore the scroll offset of `target` across content changes,
27
+ * keyed by the content its screen is showing plus an optional `options.key`.
28
+ *
29
+ * Behaviour (native):
30
+ * - Offsets are recorded from the returned `onScroll` binding, which the caller
31
+ * wires onto the list. There is no other way to observe a list's offset.
32
+ * - When the storage key changes — a re-keyed list, a remount, or `enabled`
33
+ * turning on — the saved offset is applied, or 0 when the key was never seen.
34
+ * - A push/pop that re-focuses the same screen scrolls nothing. The navigator
35
+ * already preserved the position, and re-applying it would at best be a
36
+ * no-op and at worst fight a list that has legitimately moved on.
20
37
  */
21
- export function ScrollRestorationProvider({
22
- children
23
- }) {
24
- return children;
25
- }
38
+ export function useScrollRestoration(target, options) {
39
+ const {
40
+ store,
41
+ adapter
42
+ } = useScrollRestorationContext();
43
+ const contentId = adapter.useScreenContentId();
44
+ const enabled = options?.enabled ?? true;
45
+ const scrollKey = deriveScrollKey(contentId, options?.key);
26
46
 
27
- /**
28
- * No-op hook. Accepts the same arguments as the web implementation so call
29
- * sites are identical across platforms.
30
- */
31
- export function useScrollRestoration(_target, _options) {
32
- // Intentionally empty: native-stack already preserves scroll position.
47
+ // The key the live `onScroll` binding is allowed to record against. It is
48
+ // assigned by the effect below only AFTER that effect's own write has landed,
49
+ // and cleared by its cleanup — so the relayout the outgoing content emits
50
+ // while a new key is already rendered is never attributed to the new key.
51
+ const recordingKeyRef = useRef(null);
52
+ useEffect(() => {
53
+ if (!enabled || scrollKey === null) return undefined;
54
+ const scroller = createScroller(target);
55
+ let frame = null;
56
+
57
+ // Restore and reset are ONE write: a miss reads 0, and 0 is exactly what a
58
+ // list showing content never seen this session must be sent to rather than
59
+ // left at the offset its predecessor had.
60
+ const apply = () => {
61
+ frame = null;
62
+ scroller.setOffset(store.read(scrollKey));
63
+ recordingKeyRef.current = scrollKey;
64
+ };
65
+
66
+ // Defer one frame. This effect fires in the same commit that rendered the
67
+ // incoming rows, and a list cannot honour an offset before it has laid them
68
+ // out — the write would be clamped to whatever height it has reached so
69
+ // far, with no second chance (unlike web, native gets no scroll event to
70
+ // re-apply from). One frame is all that is needed because `enabled` has
71
+ // already guaranteed the rows are there; what remains is the layout pass.
72
+ if (typeof requestAnimationFrame === 'function') {
73
+ frame = requestAnimationFrame(apply);
74
+ } else {
75
+ // No frame scheduler (SSR, or a bare Node import of this barrel).
76
+ // Applying immediately is still the correct write; only the wait is lost.
77
+ apply();
78
+ }
79
+ return () => {
80
+ if (frame !== null) cancelAnimationFrame(frame);
81
+ recordingKeyRef.current = null;
82
+ };
83
+ }, [store, scrollKey, enabled, target]);
84
+ const onScroll = useCallback(event => {
85
+ const key = recordingKeyRef.current;
86
+ if (key === null) return;
87
+ store.save(key, event.nativeEvent.contentOffset.y);
88
+ }, [store]);
89
+ return useMemo(() => ({
90
+ onScroll
91
+ }), [onScroll]);
33
92
  }
34
93
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ScrollRestorationProvider","children","useScrollRestoration","_target","_options"],"sourceRoot":"../../../src","sources":["scroll/index.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAeA;AACA;AACA;AACA;AACA,OAAO,SAASA,yBAAyBA,CAAC;EACxCC;AAC8B,CAAC,EAAgB;EAC/C,OAAOA,QAAQ;AACjB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAClCC,OAAgC,EAChCC,QAAsC,EAChC;EACN;AAAA","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useScrollRestorationContext","createScroller","deriveScrollKey","ScrollRestorationProvider","useScrollRestoration","target","options","store","adapter","contentId","useScreenContentId","enabled","scrollKey","key","recordingKeyRef","undefined","scroller","frame","apply","setOffset","read","current","requestAnimationFrame","cancelAnimationFrame","onScroll","event","save","nativeEvent","contentOffset","y"],"sourceRoot":"../../../src","sources":["scroll/index.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,QAAQ,OAAO;AAE/D,SAASC,2BAA2B,QAAQ,cAAW;AACvD,SAASC,cAAc,QAAQ,wBAAqB;AACpD,SAASC,eAAe,QAAQ,YAAS;AAOzC,SAASC,yBAAyB,QAAQ,cAAW;AAYrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAClCC,MAA+B,EAC/BC,OAAqC,EACX;EAC1B,MAAM;IAAEC,KAAK;IAAEC;EAAQ,CAAC,GAAGR,2BAA2B,CAAC,CAAC;EACxD,MAAMS,SAAS,GAAGD,OAAO,CAACE,kBAAkB,CAAC,CAAC;EAC9C,MAAMC,OAAO,GAAGL,OAAO,EAAEK,OAAO,IAAI,IAAI;EACxC,MAAMC,SAAS,GAAGV,eAAe,CAACO,SAAS,EAAEH,OAAO,EAAEO,GAAG,CAAC;;EAE1D;EACA;EACA;EACA;EACA,MAAMC,eAAe,GAAGf,MAAM,CAAgB,IAAI,CAAC;EAEnDF,SAAS,CAAC,MAAM;IACd,IAAI,CAACc,OAAO,IAAIC,SAAS,KAAK,IAAI,EAAE,OAAOG,SAAS;IAEpD,MAAMC,QAAQ,GAAGf,cAAc,CAACI,MAAM,CAAC;IACvC,IAAIY,KAAoB,GAAG,IAAI;;IAE/B;IACA;IACA;IACA,MAAMC,KAAK,GAAGA,CAAA,KAAY;MACxBD,KAAK,GAAG,IAAI;MACZD,QAAQ,CAACG,SAAS,CAACZ,KAAK,CAACa,IAAI,CAACR,SAAS,CAAC,CAAC;MACzCE,eAAe,CAACO,OAAO,GAAGT,SAAS;IACrC,CAAC;;IAED;IACA;IACA;IACA;IACA;IACA;IACA,IAAI,OAAOU,qBAAqB,KAAK,UAAU,EAAE;MAC/CL,KAAK,GAAGK,qBAAqB,CAACJ,KAAK,CAAC;IACtC,CAAC,MAAM;MACL;MACA;MACAA,KAAK,CAAC,CAAC;IACT;IAEA,OAAO,MAAM;MACX,IAAID,KAAK,KAAK,IAAI,EAAEM,oBAAoB,CAACN,KAAK,CAAC;MAC/CH,eAAe,CAACO,OAAO,GAAG,IAAI;IAChC,CAAC;EACH,CAAC,EAAE,CAACd,KAAK,EAAEK,SAAS,EAAED,OAAO,EAAEN,MAAM,CAAC,CAAC;EAEvC,MAAMmB,QAAQ,GAAG5B,WAAW,CACzB6B,KAAK,IAAK;IACT,MAAMZ,GAAG,GAAGC,eAAe,CAACO,OAAO;IACnC,IAAIR,GAAG,KAAK,IAAI,EAAE;IAClBN,KAAK,CAACmB,IAAI,CAACb,GAAG,EAAEY,KAAK,CAACE,WAAW,CAACC,aAAa,CAACC,CAAC,CAAC;EACpD,CAAC,EACD,CAACtB,KAAK,CACR,CAAC;EAED,OAAOT,OAAO,CAAC,OAAO;IAAE0B;EAAS,CAAC,CAAC,EAAE,CAACA,QAAQ,CAAC,CAAC;AAClD","ignoreList":[]}
@@ -3,22 +3,22 @@
3
3
  /**
4
4
  * Web variant of the scroll-restoration primitive.
5
5
  *
6
- * Mirrors the proven Bluesky pattern (`history.scrollRestoration = 'manual'`
7
- * plus an in-memory `Map<routeKey, offset>`) with two deliberate differences
8
- * forced by Oxy's layouts and the behaviour of the (expo-router-wrapped)
9
- * React Navigation web stack:
6
+ * Mirrors the proven Bluesky pattern (manual `history.scrollRestoration` plus
7
+ * an in-memory `Map<key, offset>`) with three deliberate differences forced by
8
+ * Oxy's layouts and by the behaviour of the (expo-router-wrapped) React
9
+ * Navigation web stack:
10
10
  *
11
11
  * 1. Bluesky restores the WINDOW scroller, whereas Oxy apps keep multi-column
12
12
  * layouts whose feed scrolls an INNER container. So we restore the offset
13
13
  * of a caller-registered scrollable (a ref to an element / RN scroll
14
- * component, or the `'window'` sentinel), keyed by the active route.
14
+ * component, or the `'window'` sentinel).
15
15
  *
16
- * 2. The web stack HIDES the background screen on push. While
17
- * hidden, the previous screen's scroll container collapses
18
- * (`scrollHeight === clientHeight`) and the navigator forces its
19
- * `scrollTop` to 0. The screen is NOT unmounted, so a virtualized list
20
- * (e.g. FlashList) keeps its rows but re-lays them out over SEVERAL frames
21
- * once the screen is re-shown. Two problems follow, both handled here:
16
+ * 2. The web stack HIDES the background screen on push. While hidden, the
17
+ * previous screen's scroll container collapses (`scrollHeight ===
18
+ * clientHeight`) and the navigator forces its `scrollTop` to 0. The screen
19
+ * is NOT unmounted, so a virtualized list (e.g. FlashList) keeps its rows
20
+ * but re-lays them out over SEVERAL frames once the screen is re-shown. Two
21
+ * problems follow, both handled here:
22
22
  *
23
23
  * (a) A blur-time read of `scrollTop` returns the navigator's forced 0,
24
24
  * not the user's real offset — saving it would clobber the good
@@ -32,32 +32,26 @@
32
32
  * sticks (the content has grown tall enough) or a small frame cap is
33
33
  * reached.
34
34
  *
35
- * Native bundlers use `./index.ts` (a no-op); web bundlers select this file via
36
- * the `"browser"` export condition in `package.json`.
35
+ * 3. A document-scrolled app (one window scroller shared by every route) has
36
+ * no per-screen container to reset, so an unrecognised key MUST be written
37
+ * to 0 explicitly. Doing nothing leaves the previous screen's offset in
38
+ * place and the new screen opens mid-page.
37
39
  *
38
- * The navigation hooks are imported from `expo-router` (which re-exports
39
- * `useFocusEffect` and `useRoute` from its bundled React Navigation core) rather
40
- * than from `@react-navigation/native` directly. Every Oxy app uses expo-router
41
- * as its router, so it is always a DIRECT, top-level dependency that resolves
42
- * cleanly under Bun's isolated linker — whereas `@react-navigation/native` is
43
- * only a nested/transitive dependency of expo-router and would fail to resolve
44
- * when bundling those apps.
40
+ * Native bundlers use `./index.ts`; web bundlers select this file via the
41
+ * `"browser"` export condition in `package.json`.
45
42
  */
46
- import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
47
- import { useFocusEffect, useRoute } from 'expo-router';
43
+ import { useCallback, useRef } from 'react';
44
+ import { useScrollRestorationContext } from "./context.js";
48
45
  import { createScroller } from "./scrollable.web.js";
49
- import { ScrollOffsetStore, deriveScrollKey } from "./store.js";
50
- import { jsx as _jsx } from "react/jsx-runtime";
51
- const ScrollOffsetContext = /*#__PURE__*/createContext(null);
52
- ScrollOffsetContext.displayName = 'BloomScrollOffsetContext';
53
-
46
+ import { deriveScrollKey } from "./store.js";
47
+ export { ScrollRestorationProvider } from "./context.js";
54
48
  /**
55
- * Maximum number of animation frames the focus restore will re-apply the saved
56
- * offset before giving up. A virtualized list re-lays out its rows over a
57
- * handful of frames after its screen is re-shown; ~30 frames (≈0.5s at 60fps)
58
- * is comfortably longer than any observed relayout while staying short enough
59
- * that the loop never lingers as a perceptible cost. The loop normally exits
60
- * far earlier — as soon as the write sticks.
49
+ * Maximum number of animation frames the restore will re-apply the saved offset
50
+ * before giving up. A virtualized list re-lays out its rows over a handful of
51
+ * frames after its screen is re-shown; ~30 frames (≈0.5s at 60fps) is
52
+ * comfortably longer than any observed relayout while staying short enough that
53
+ * the loop never lingers as a perceptible cost. The loop normally exits far
54
+ * earlier — as soon as the write sticks.
61
55
  */
62
56
  const RESTORE_FRAME_CAP = 30;
63
57
 
@@ -71,141 +65,243 @@ const RESTORE_FRAME_CAP = 30;
71
65
  const RESTORE_STICK_TOLERANCE_PX = 2;
72
66
 
73
67
  /**
74
- * Switch the browser to manual scroll restoration exactly once per document.
75
- *
76
- * The browser's default `'auto'` restoration fights our manual restore on
77
- * Back/Forward navigations. Doing this at module scope (guarded for SSR) means
78
- * it is set before any provider mounts, matching Bluesky's module-level call.
68
+ * The web binding is a constant: this platform observes offsets by subscribing
69
+ * to the resolved DOM node's own `scroll` event, so it needs nothing from the
70
+ * caller. It is still returned and still safe to spread onto a list — so a
71
+ * call site is written once and runs on both platforms.
79
72
  */
80
- if (typeof history !== 'undefined' && 'scrollRestoration' in history) {
81
- history.scrollRestoration = 'manual';
82
- }
73
+ const WEB_BINDING = {
74
+ onScroll: () => undefined
75
+ };
83
76
 
84
77
  /**
85
- * Holds the per-route offset map for the subtree. One provider near the app
86
- * root is enough; the store lives for the document's lifetime so offsets
87
- * survive navigating away and back (including browser Back/Forward).
78
+ * Events that mean the USER has taken over the scroller, and the restore must
79
+ * stop trying to put them back.
80
+ *
81
+ * The browser's own restoration is specified the same way: HTML §7.4.6.5 has
82
+ * the UA re-attempt the saved position and "may continue to attempt to do so
83
+ * periodically, until document's has been scrolled by the user becomes true".
84
+ * Without this the loop fights anyone who starts scrolling inside the frame
85
+ * budget, yanking them back once per frame for up to {@link RESTORE_FRAME_CAP}
86
+ * frames.
87
+ *
88
+ * Two designs were rejected, and the reasons are the point:
89
+ *
90
+ * - **"Any `scroll` event aborts"** — the obvious version, and it aborts on the
91
+ * FIRST frame every time: `setOffset` is itself a scroll, so the loop would
92
+ * cancel on the event its own write produced and no restore would ever
93
+ * complete.
94
+ * - **"Abort on an offset we did not write"** — tempting because it needs no
95
+ * listeners, but a virtualized list inserting rows ABOVE the viewport makes
96
+ * the browser's scroll anchoring move `scrollTop` on its own. That is
97
+ * precisely the situation the loop exists for, so the unexplained-delta test
98
+ * would abort exactly when it must not.
99
+ *
100
+ * What is being detected is user INPUT, so this detects user input. `pointerdown`
101
+ * covers a scrollbar drag and a touch-less press; `keydown` covers arrow/page
102
+ * keys, and reaches us whenever focus is inside the scroller it would scroll.
88
103
  */
89
- export function ScrollRestorationProvider({
90
- children
91
- }) {
92
- const store = useMemo(() => new ScrollOffsetStore(), []);
93
- return /*#__PURE__*/_jsx(ScrollOffsetContext.Provider, {
94
- value: store,
95
- children: children
96
- });
97
- }
98
- function useScrollOffsetStore() {
99
- const store = useContext(ScrollOffsetContext);
100
- if (store === null) {
101
- throw new Error('useScrollRestoration must be used within a <ScrollRestorationProvider>.');
102
- }
103
- return store;
104
- }
104
+ const USER_TAKEOVER_EVENTS = ['wheel', 'touchstart', 'pointerdown', 'keydown'];
105
105
 
106
106
  /**
107
107
  * Preserve and restore the scroll offset of `target` across navigation, keyed
108
- * by the active route (plus an optional `options.key` for routes that host
109
- * multiple scrollables).
108
+ * by the content its screen is showing plus an optional `options.key`.
110
109
  *
111
110
  * Behaviour (web):
112
111
  * - On every scroll while the screen is focused, the current offset is recorded
113
- * in memory and persisted. This live stream of saves is the source of truth.
114
- * - On focus, the saved offset is re-applied across a bounded run of animation
115
- * frames, stopping as soon as the write sticks (the list has re-rendered its
116
- * rows and grown tall enough) or {@link RESTORE_FRAME_CAP} is reached. A
117
- * saved offset of 0 is a no-op (nothing to restore).
118
- * - On blur, the LAST OBSERVED offset is persisted as a final safety net — not
119
- * a fresh `scrollTop` read, which the navigator may already have forced to 0
120
- * while collapsing the hidden screen.
112
+ * under the key that was live when the listener was attached. This stream of
113
+ * saves is the source of truth.
114
+ * - On focus and again whenever the key changes while the screen STAYS
115
+ * focused, which is how an in-screen tab or folder swap is seen at all — the
116
+ * offset saved for the new key is re-applied across a bounded run of
117
+ * animation frames, stopping as soon as the write sticks or
118
+ * {@link RESTORE_FRAME_CAP} is reached. Content never seen this session is
119
+ * written to 0 once instead; a write to 0 always sticks, so it needs no loop.
120
+ * - On blur (and on a key change), the LAST OBSERVED offset is persisted under
121
+ * the OUTGOING key as a final safety net — not a fresh `scrollTop` read,
122
+ * which the navigator may already have forced to 0 while collapsing the
123
+ * hidden screen.
121
124
  */
122
125
  export function useScrollRestoration(target, options) {
123
- const store = useScrollOffsetStore();
124
- const route = useRoute();
125
- const subKey = options?.key;
126
+ const {
127
+ store,
128
+ adapter
129
+ } = useScrollRestorationContext();
130
+ const contentId = adapter.useScreenContentId();
126
131
  const enabled = options?.enabled ?? true;
127
- const scrollKey = deriveScrollKey(route.key, subKey);
132
+ const scrollKey = deriveScrollKey(contentId, options?.key);
128
133
 
129
- // Keep the latest target/enabled/key in refs so the focus effect can read
130
- // them without being re-subscribed on every render.
131
- const targetRef = useRef(target);
132
- targetRef.current = target;
133
- const enabledRef = useRef(enabled);
134
- enabledRef.current = enabled;
135
- const scrollKeyRef = useRef(scrollKey);
136
- scrollKeyRef.current = scrollKey;
137
- useFocusEffect(
138
- // The effect identity is intentionally stable across renders: it reads all
139
- // varying inputs from refs. expo-router's `useFocusEffect` re-runs it on
140
- // each focus.
134
+ // The key this hook instance has already sent to the top. Writing 0 is a
135
+ // one-time ARRIVAL action: a later re-run for the SAME key (`enabled` turning
136
+ // on once the rows exist, a screen regaining focus) must not repeat it,
137
+ // because by then the user may have scrolled somewhere the hook never got to
138
+ // record. Restoring twice is harmless; resetting twice is data loss.
139
+ const resetKeyRef = useRef(null);
140
+ adapter.useScreenFocusEffect(
141
+ // Every varying input is a dependency rather than a ref read, so the
142
+ // session below CLOSES OVER the key it belongs to. That is what makes a
143
+ // key change mid-focus correct in both directions: the outgoing session's
144
+ // cleanup persists to the old key, and the incoming one restores the new.
141
145
  useCallback(() => {
142
- const key = scrollKeyRef.current;
143
- if (!enabledRef.current || key === null) return undefined;
144
- const scroller = createScroller(targetRef.current);
145
- const element = targetRef.current === 'window' ? typeof window !== 'undefined' ? window : null : resolveScrollEventTarget(targetRef.current);
146
+ if (!enabled || scrollKey === null) return undefined;
147
+ const scroller = createScroller(target);
148
+ const element = target === 'window' ? typeof window === 'undefined' ? null : window : resolveScrollEventTarget(target);
146
149
 
147
- // The last offset the live scroll listener observed for this focus
148
- // session. This — not a blur-time `getOffset()` — is what we persist on
149
- // blur, because by blur time the navigator may have collapsed the
150
- // hidden screen and forced its `scrollTop` to 0 (bug A). `null` means
151
- // the user never scrolled this session, so there is nothing newer to
152
- // persist than what the scroll listener already saved live.
150
+ // The last offset the live scroll listener observed for this session.
151
+ // This — not a cleanup-time `getOffset()` — is what we persist at the
152
+ // end, because by then the navigator may have collapsed the hidden
153
+ // screen and forced its `scrollTop` to 0 (problem A). `null` means the
154
+ // user never scrolled this session, so there is nothing newer to persist
155
+ // than what the scroll listener already saved live.
153
156
  let lastObservedOffset = null;
157
+
158
+ // The offset WE last wrote, which a real browser dispatches back as a
159
+ // `scroll` event (jsdom does not, so the tests for this emit it
160
+ // explicitly). Both writers arm it: the reset, and every frame of the
161
+ // restore loop. Persisting either records an offset the user never
162
+ // chose — the reset's 0 under a key another LIVE screen may share (the
163
+ // two-live-entries trade content keying makes, see `deriveScrollKey`),
164
+ // and the loop's intermediate value as a PARTIAL that outlives an
165
+ // interrupted restore.
166
+ //
167
+ // A `restoring` FLAG covering the loop's duration is the obvious
168
+ // alternative and is wrong. It could only be cleared by a takeover
169
+ // event, so a session whose scroll came from a source that fires none —
170
+ // `scrollIntoView` from another component, a focus-driven scroll — would
171
+ // suppress saves indefinitely and never record the user's position at
172
+ // all: an unbounded corruption traded for a bounded one. Arming a VALUE
173
+ // is bounded to one event per write and covers every way the loop can
174
+ // end (stuck, capped, aborted), because each frame re-arms.
175
+ let echoOffset = null;
176
+
177
+ // The largest offset reachable when we last PERSISTED one. A browser
178
+ // clamp is recognised against this rather than against the previous
179
+ // OBSERVATION, and that distinction is the whole mechanism: Chrome
180
+ // dispatches TWO `scroll` events for one clamp, so an
181
+ // observation-relative test sees the shrink on the first and nothing on
182
+ // the second, which then saves the clamped value anyway. Measured.
183
+ let maxOffsetAtLastSave = scroller.getMaxOffset();
154
184
  const save = () => {
155
- const currentKey = scrollKeyRef.current;
156
- if (!enabledRef.current || currentKey === null) return;
157
185
  const offset = scroller.getOffset();
186
+ const maxOffset = scroller.getMaxOffset();
158
187
  // Ignore a spurious 0 produced by the navigator collapsing a hidden
159
188
  // background screen: while collapsed the container cannot scroll, so
160
- // its `scrollTop` is forced to 0. Persisting it would clobber the
161
- // good offset recorded by earlier live saves (bug A). A genuine
189
+ // its `scrollTop` is forced to 0. Persisting it would clobber the good
190
+ // offset recorded by earlier live saves (problem A). A genuine
162
191
  // scroll-to-top keeps the container scrollable and is saved normally.
163
- if (offset === 0 && !scroller.canScroll()) return;
192
+ if (offset === 0 && maxOffset <= 0) return;
193
+ // A partial clamp: the reachable range SHRANK and the offset is sitting
194
+ // exactly at the new bottom. That is what the browser does to a tab
195
+ // whose document has been taken over by a shorter sibling — the two
196
+ // together are a signature a genuine scroll does not have, because
197
+ // reaching the bottom on your own does not shrink the page in the same
198
+ // breath. Suppressing keeps the pre-clamp offset, which on return
199
+ // simply clamps to the same place if the content really did shrink.
200
+ if (maxOffset < maxOffsetAtLastSave && Math.abs(offset - maxOffset) <= RESTORE_STICK_TOLERANCE_PX) {
201
+ return;
202
+ }
203
+ if (offset === echoOffset) {
204
+ // Our own write coming back. Consume the arming so a real scroll to
205
+ // the same offset later is saved normally.
206
+ echoOffset = null;
207
+ return;
208
+ }
209
+ echoOffset = null;
164
210
  lastObservedOffset = offset;
165
- store.save(currentKey, offset);
211
+ maxOffsetAtLastSave = maxOffset;
212
+ store.save(scrollKey, offset);
166
213
  };
167
214
 
168
- // Restore across a bounded run of frames. A freshly re-shown
169
- // virtualized list re-lays out its rows over several frames, so a
170
- // single write while it is still collapsed would be clamped to 0 and
171
- // never re-applied (bug B). We re-apply each frame until the write
172
- // sticks or the frame cap is hit.
173
- const targetOffset = store.read(key);
215
+ // Restore and reset are ONE write. A miss reads 0, and 0 is exactly what
216
+ // an unseen screen must be written to left alone it would show the
217
+ // previous screen's offset, since a document-scrolled app shares one
218
+ // scroller across every route.
219
+ const targetOffset = store.read(scrollKey);
220
+
221
+ // They remain two DECISIONS, though, and `read` cannot tell them apart:
222
+ // a key never seen and a key deliberately saved at the top both read 0.
223
+ // Only the first is a RESET, and a reset is a one-time ARRIVAL action
224
+ // gated on `resetKeyRef` — repeating it on a later re-run for the same
225
+ // key would send a user who has since scrolled back to the top, and the
226
+ // hook was inert while they did it so there is nothing to restore them
227
+ // to afterwards. A 0 the user deliberately saved is theirs, and always
228
+ // re-applies. `has` is the only thing that answers this.
229
+ const isReset = !store.has(scrollKey);
230
+ const alreadyReset = isReset && resetKeyRef.current === scrollKey;
231
+ if (isReset) resetKeyRef.current = scrollKey;
174
232
  let rafId = null;
175
- if (targetOffset > 0 && typeof requestAnimationFrame !== 'undefined') {
233
+ const stopRestore = () => {
234
+ if (rafId === null) return;
235
+ cancelAnimationFrame(rafId);
236
+ rafId = null;
237
+ };
238
+ if (alreadyReset) {
239
+ // Nothing to write: this content has already been sent to the top once
240
+ // and the user is wherever they have moved to since.
241
+ } else if (targetOffset === 0) {
242
+ // A write to 0 is never clamped away, so it needs no re-apply loop.
243
+ echoOffset = 0;
244
+ scroller.setOffset(0);
245
+ } else if (typeof requestAnimationFrame !== 'undefined') {
246
+ // Re-apply across a bounded run of frames. A freshly re-shown
247
+ // virtualized list re-lays out its rows over several frames, so a
248
+ // single write while it is still collapsed would be clamped to 0 and
249
+ // never re-applied (problem B).
250
+ //
251
+ // Every write the loop makes is suppressed from the save path (see
252
+ // `echoOffset` below), so an interrupted restore leaves the ORIGINAL
253
+ // target stored rather than a partial. Nothing is lost on the success
254
+ // path: `targetOffset` came out of the store, so persisting it again
255
+ // would write back the same number.
176
256
  let framesLeft = RESTORE_FRAME_CAP;
177
257
  const applyOffset = () => {
178
258
  rafId = null;
179
259
  scroller.setOffset(targetOffset);
180
260
  framesLeft -= 1;
261
+ // `getOffset` re-reads the value the browser actually took, which is
262
+ // clamped to the content height it has reached so far. Arm it as the
263
+ // echo to swallow: this write is about to come back as a `scroll`
264
+ // event, and persisting a PARTIAL offset we wrote ourselves is how a
265
+ // restore that is aborted or capped corrupts the stored position.
266
+ const landed = scroller.getOffset();
267
+ echoOffset = landed;
181
268
  // Stop once the write took effect (content grew tall enough) or we
182
- // exhaust the frame budget. `getOffset` re-reads the clamped value.
183
- const reached = Math.abs(scroller.getOffset() - targetOffset) <= RESTORE_STICK_TOLERANCE_PX;
269
+ // exhaust the frame budget.
270
+ const reached = Math.abs(landed - targetOffset) <= RESTORE_STICK_TOLERANCE_PX;
184
271
  if (!reached && framesLeft > 0) {
185
272
  rafId = requestAnimationFrame(applyOffset);
186
273
  }
187
274
  };
188
275
  rafId = requestAnimationFrame(applyOffset);
276
+ // Only while a loop is actually in flight — the reset path has nothing
277
+ // to abort, and the listeners would be idle on every screen.
278
+ for (const type of USER_TAKEOVER_EVENTS) {
279
+ element?.addEventListener(type, stopRestore, {
280
+ passive: true
281
+ });
282
+ }
189
283
  }
190
284
  element?.addEventListener('scroll', save, {
191
285
  passive: true
192
286
  });
193
287
  return () => {
194
- if (rafId !== null) cancelAnimationFrame(rafId);
288
+ stopRestore();
195
289
  element?.removeEventListener('scroll', save);
196
- // Final capture on blur: persist the last offset the scroll listener
197
- // OBSERVED, never a fresh read (which the navigator may have forced
198
- // to 0 while collapsing the hidden screen). When the user never
199
- // scrolled this session there is nothing newer to persist than the
200
- // live saves already recorded.
290
+ // Removing a listener that was never added is a no-op, so this needs no
291
+ // flag tracking whether the loop branch ran.
292
+ for (const type of USER_TAKEOVER_EVENTS) {
293
+ element?.removeEventListener(type, stopRestore);
294
+ }
295
+ // Final capture: persist the last offset the scroll listener OBSERVED,
296
+ // never a fresh read (which the navigator may have forced to 0 while
297
+ // collapsing the hidden screen). When the user never scrolled this
298
+ // session there is nothing newer than the live saves already recorded.
201
299
  if (lastObservedOffset !== null) {
202
- const currentKey = scrollKeyRef.current;
203
- if (enabledRef.current && currentKey !== null) {
204
- store.save(currentKey, lastObservedOffset);
205
- }
300
+ store.save(scrollKey, lastObservedOffset);
206
301
  }
207
302
  };
208
- }, [store]));
303
+ }, [store, scrollKey, enabled, target]));
304
+ return WEB_BINDING;
209
305
  }
210
306
 
211
307
  /**
@@ -1 +1 @@
1
- {"version":3,"names":["createContext","useCallback","useContext","useMemo","useRef","useFocusEffect","useRoute","createScroller","ScrollOffsetStore","deriveScrollKey","jsx","_jsx","ScrollOffsetContext","displayName","RESTORE_FRAME_CAP","RESTORE_STICK_TOLERANCE_PX","history","scrollRestoration","ScrollRestorationProvider","children","store","Provider","value","useScrollOffsetStore","Error","useScrollRestoration","target","options","route","subKey","key","enabled","scrollKey","targetRef","current","enabledRef","scrollKeyRef","undefined","scroller","element","window","resolveScrollEventTarget","lastObservedOffset","save","currentKey","offset","getOffset","canScroll","targetOffset","read","rafId","requestAnimationFrame","framesLeft","applyOffset","setOffset","reached","Math","abs","addEventListener","passive","cancelAnimationFrame","removeEventListener","EventTarget","handle","getScrollableNode","node"],"sourceRoot":"../../../src","sources":["scroll/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,aAAa,EAAEC,WAAW,EAAEC,UAAU,EAAEC,OAAO,EAAEC,MAAM,QAAQ,OAAO;AAC/E,SAASC,cAAc,EAAEC,QAAQ,QAAQ,aAAa;AAEtD,SAASC,cAAc,QAAQ,qBAAkB;AACjD,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,YAAS;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAc7D,MAAMC,mBAAmB,gBAAGZ,aAAa,CAA2B,IAAI,CAAC;AACzEY,mBAAmB,CAACC,WAAW,GAAG,0BAA0B;;AAE5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,iBAAiB,GAAG,EAAE;;AAE5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,0BAA0B,GAAG,CAAC;;AAEpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,OAAOC,OAAO,KAAK,WAAW,IAAI,mBAAmB,IAAIA,OAAO,EAAE;EACpEA,OAAO,CAACC,iBAAiB,GAAG,QAAQ;AACtC;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,yBAAyBA,CAAC;EACxCC;AAC8B,CAAC,EAAE;EACjC,MAAMC,KAAK,GAAGjB,OAAO,CAAC,MAAM,IAAIK,iBAAiB,CAAC,CAAC,EAAE,EAAE,CAAC;EACxD,oBACEG,IAAA,CAACC,mBAAmB,CAACS,QAAQ;IAACC,KAAK,EAAEF,KAAM;IAAAD,QAAA,EACxCA;EAAQ,CACmB,CAAC;AAEnC;AAEA,SAASI,oBAAoBA,CAAA,EAAsB;EACjD,MAAMH,KAAK,GAAGlB,UAAU,CAACU,mBAAmB,CAAC;EAC7C,IAAIQ,KAAK,KAAK,IAAI,EAAE;IAClB,MAAM,IAAII,KAAK,CACb,yEACF,CAAC;EACH;EACA,OAAOJ,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASK,oBAAoBA,CAClCC,MAA+B,EAC/BC,OAAqC,EAC/B;EACN,MAAMP,KAAK,GAAGG,oBAAoB,CAAC,CAAC;EACpC,MAAMK,KAAK,GAAGtB,QAAQ,CAAC,CAAC;EACxB,MAAMuB,MAAM,GAAGF,OAAO,EAAEG,GAAG;EAC3B,MAAMC,OAAO,GAAGJ,OAAO,EAAEI,OAAO,IAAI,IAAI;EAExC,MAAMC,SAAS,GAAGvB,eAAe,CAACmB,KAAK,CAACE,GAAG,EAAED,MAAM,CAAC;;EAEpD;EACA;EACA,MAAMI,SAAS,GAAG7B,MAAM,CAACsB,MAAM,CAAC;EAChCO,SAAS,CAACC,OAAO,GAAGR,MAAM;EAC1B,MAAMS,UAAU,GAAG/B,MAAM,CAAC2B,OAAO,CAAC;EAClCI,UAAU,CAACD,OAAO,GAAGH,OAAO;EAC5B,MAAMK,YAAY,GAAGhC,MAAM,CAAC4B,SAAS,CAAC;EACtCI,YAAY,CAACF,OAAO,GAAGF,SAAS;EAEhC3B,cAAc;EACZ;EACA;EACA;EACAJ,WAAW,CACT,MAAM;IACJ,MAAM6B,GAAG,GAAGM,YAAY,CAACF,OAAO;IAChC,IAAI,CAACC,UAAU,CAACD,OAAO,IAAIJ,GAAG,KAAK,IAAI,EAAE,OAAOO,SAAS;IAEzD,MAAMC,QAAQ,GAAG/B,cAAc,CAAC0B,SAAS,CAACC,OAAO,CAAC;IAClD,MAAMK,OAAO,GACXN,SAAS,CAACC,OAAO,KAAK,QAAQ,GACzB,OAAOM,MAAM,KAAK,WAAW,GAAGA,MAAM,GAAG,IAAI,GAC9CC,wBAAwB,CAACR,SAAS,CAACC,OAAO,CAAC;;IAEjD;IACA;IACA;IACA;IACA;IACA;IACA,IAAIQ,kBAAiC,GAAG,IAAI;IAE5C,MAAMC,IAAI,GAAGA,CAAA,KAAM;MACjB,MAAMC,UAAU,GAAGR,YAAY,CAACF,OAAO;MACvC,IAAI,CAACC,UAAU,CAACD,OAAO,IAAIU,UAAU,KAAK,IAAI,EAAE;MAChD,MAAMC,MAAM,GAAGP,QAAQ,CAACQ,SAAS,CAAC,CAAC;MACnC;MACA;MACA;MACA;MACA;MACA,IAAID,MAAM,KAAK,CAAC,IAAI,CAACP,QAAQ,CAACS,SAAS,CAAC,CAAC,EAAE;MAC3CL,kBAAkB,GAAGG,MAAM;MAC3BzB,KAAK,CAACuB,IAAI,CAACC,UAAU,EAAEC,MAAM,CAAC;IAChC,CAAC;;IAED;IACA;IACA;IACA;IACA;IACA,MAAMG,YAAY,GAAG5B,KAAK,CAAC6B,IAAI,CAACnB,GAAG,CAAC;IACpC,IAAIoB,KAAoB,GAAG,IAAI;IAE/B,IAAIF,YAAY,GAAG,CAAC,IAAI,OAAOG,qBAAqB,KAAK,WAAW,EAAE;MACpE,IAAIC,UAAU,GAAGtC,iBAAiB;MAClC,MAAMuC,WAAW,GAAGA,CAAA,KAAM;QACxBH,KAAK,GAAG,IAAI;QACZZ,QAAQ,CAACgB,SAAS,CAACN,YAAY,CAAC;QAChCI,UAAU,IAAI,CAAC;QACf;QACA;QACA,MAAMG,OAAO,GACXC,IAAI,CAACC,GAAG,CAACnB,QAAQ,CAACQ,SAAS,CAAC,CAAC,GAAGE,YAAY,CAAC,IAC7CjC,0BAA0B;QAC5B,IAAI,CAACwC,OAAO,IAAIH,UAAU,GAAG,CAAC,EAAE;UAC9BF,KAAK,GAAGC,qBAAqB,CAACE,WAAW,CAAC;QAC5C;MACF,CAAC;MACDH,KAAK,GAAGC,qBAAqB,CAACE,WAAW,CAAC;IAC5C;IAEAd,OAAO,EAAEmB,gBAAgB,CAAC,QAAQ,EAAEf,IAAI,EAAE;MAAEgB,OAAO,EAAE;IAAK,CAAC,CAAC;IAE5D,OAAO,MAAM;MACX,IAAIT,KAAK,KAAK,IAAI,EAAEU,oBAAoB,CAACV,KAAK,CAAC;MAC/CX,OAAO,EAAEsB,mBAAmB,CAAC,QAAQ,EAAElB,IAAI,CAAC;MAC5C;MACA;MACA;MACA;MACA;MACA,IAAID,kBAAkB,KAAK,IAAI,EAAE;QAC/B,MAAME,UAAU,GAAGR,YAAY,CAACF,OAAO;QACvC,IAAIC,UAAU,CAACD,OAAO,IAAIU,UAAU,KAAK,IAAI,EAAE;UAC7CxB,KAAK,CAACuB,IAAI,CAACC,UAAU,EAAEF,kBAAkB,CAAC;QAC5C;MACF;IACF,CAAC;EACH,CAAC,EACD,CAACtB,KAAK,CACR,CACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA,SAASqB,wBAAwBA,CAC/Bf,MAAkD,EAC9B;EACpB,MAAMQ,OAAO,GAAIR,MAAM,CAA0BQ,OAAO;EACxD,IAAIA,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI;EAChC,IAAI,OAAO4B,WAAW,KAAK,WAAW,IAAI5B,OAAO,YAAY4B,WAAW,EAAE;IACxE,OAAO5B,OAAO;EAChB;EACA,MAAM6B,MAAM,GAAG7B,OAAgD;EAC/D,IAAI,OAAO6B,MAAM,CAACC,iBAAiB,KAAK,UAAU,EAAE;IAClD,MAAMC,IAAI,GAAGF,MAAM,CAACC,iBAAiB,CAAC,CAAC;IACvC,IAAI,OAAOF,WAAW,KAAK,WAAW,IAAIG,IAAI,YAAYH,WAAW,EAAE;MACrE,OAAOG,IAAI;IACb;EACF;EACA,OAAO,IAAI;AACb","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useRef","useScrollRestorationContext","createScroller","deriveScrollKey","ScrollRestorationProvider","RESTORE_FRAME_CAP","RESTORE_STICK_TOLERANCE_PX","WEB_BINDING","onScroll","undefined","USER_TAKEOVER_EVENTS","useScrollRestoration","target","options","store","adapter","contentId","useScreenContentId","enabled","scrollKey","key","resetKeyRef","useScreenFocusEffect","scroller","element","window","resolveScrollEventTarget","lastObservedOffset","echoOffset","maxOffsetAtLastSave","getMaxOffset","save","offset","getOffset","maxOffset","Math","abs","targetOffset","read","isReset","has","alreadyReset","current","rafId","stopRestore","cancelAnimationFrame","setOffset","requestAnimationFrame","framesLeft","applyOffset","landed","reached","type","addEventListener","passive","removeEventListener","EventTarget","handle","getScrollableNode","node"],"sourceRoot":"../../../src","sources":["scroll/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,WAAW,EAAEC,MAAM,QAAQ,OAAO;AAE3C,SAASC,2BAA2B,QAAQ,cAAW;AACvD,SAASC,cAAc,QAAQ,qBAAkB;AACjD,SAASC,eAAe,QAAQ,YAAS;AAOzC,SAASC,yBAAyB,QAAQ,cAAW;AAYrD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,iBAAiB,GAAG,EAAE;;AAE5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,0BAA0B,GAAG,CAAC;;AAEpC;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,WAAqC,GAAG;EAAEC,QAAQ,EAAEA,CAAA,KAAMC;AAAU,CAAC;;AAE3E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,oBAAoB,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,CAAC;;AAE9E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAClCC,MAA+B,EAC/BC,OAAqC,EACX;EAC1B,MAAM;IAAEC,KAAK;IAAEC;EAAQ,CAAC,GAAGd,2BAA2B,CAAC,CAAC;EACxD,MAAMe,SAAS,GAAGD,OAAO,CAACE,kBAAkB,CAAC,CAAC;EAC9C,MAAMC,OAAO,GAAGL,OAAO,EAAEK,OAAO,IAAI,IAAI;EACxC,MAAMC,SAAS,GAAGhB,eAAe,CAACa,SAAS,EAAEH,OAAO,EAAEO,GAAG,CAAC;;EAE1D;EACA;EACA;EACA;EACA;EACA,MAAM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","ignoreList":[]}