@oxyhq/bloom 0.6.20 → 0.6.22

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 (56) hide show
  1. package/lib/commonjs/scroll/index.js +39 -0
  2. package/lib/commonjs/scroll/index.js.map +1 -0
  3. package/lib/commonjs/scroll/index.web.js +143 -0
  4. package/lib/commonjs/scroll/index.web.js.map +1 -0
  5. package/lib/commonjs/scroll/scrollable.web.js +64 -0
  6. package/lib/commonjs/scroll/scrollable.web.js.map +1 -0
  7. package/lib/commonjs/scroll/store.js +90 -0
  8. package/lib/commonjs/scroll/store.js.map +1 -0
  9. package/lib/commonjs/scroll/types.js +6 -0
  10. package/lib/commonjs/scroll/types.js.map +1 -0
  11. package/lib/commonjs/theme/color-scope/index.web.js +18 -5
  12. package/lib/commonjs/theme/color-scope/index.web.js.map +1 -1
  13. package/lib/module/scroll/index.js +34 -0
  14. package/lib/module/scroll/index.js.map +1 -0
  15. package/lib/module/scroll/index.web.js +137 -0
  16. package/lib/module/scroll/index.web.js.map +1 -0
  17. package/lib/module/scroll/scrollable.web.js +60 -0
  18. package/lib/module/scroll/scrollable.web.js.map +1 -0
  19. package/lib/module/scroll/store.js +84 -0
  20. package/lib/module/scroll/store.js.map +1 -0
  21. package/lib/module/scroll/types.js +4 -0
  22. package/lib/module/scroll/types.js.map +1 -0
  23. package/lib/module/theme/color-scope/index.web.js +18 -5
  24. package/lib/module/theme/color-scope/index.web.js.map +1 -1
  25. package/lib/typescript/commonjs/scroll/index.d.ts +27 -0
  26. package/lib/typescript/commonjs/scroll/index.d.ts.map +1 -0
  27. package/lib/typescript/commonjs/scroll/index.web.d.ts +22 -0
  28. package/lib/typescript/commonjs/scroll/index.web.d.ts.map +1 -0
  29. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts +17 -0
  30. package/lib/typescript/commonjs/scroll/scrollable.web.d.ts.map +1 -0
  31. package/lib/typescript/commonjs/scroll/store.d.ts +46 -0
  32. package/lib/typescript/commonjs/scroll/store.d.ts.map +1 -0
  33. package/lib/typescript/commonjs/scroll/types.d.ts +46 -0
  34. package/lib/typescript/commonjs/scroll/types.d.ts.map +1 -0
  35. package/lib/typescript/commonjs/theme/color-scope/index.web.d.ts.map +1 -1
  36. package/lib/typescript/module/scroll/index.d.ts +27 -0
  37. package/lib/typescript/module/scroll/index.d.ts.map +1 -0
  38. package/lib/typescript/module/scroll/index.web.d.ts +22 -0
  39. package/lib/typescript/module/scroll/index.web.d.ts.map +1 -0
  40. package/lib/typescript/module/scroll/scrollable.web.d.ts +17 -0
  41. package/lib/typescript/module/scroll/scrollable.web.d.ts.map +1 -0
  42. package/lib/typescript/module/scroll/store.d.ts +46 -0
  43. package/lib/typescript/module/scroll/store.d.ts.map +1 -0
  44. package/lib/typescript/module/scroll/types.d.ts +46 -0
  45. package/lib/typescript/module/scroll/types.d.ts.map +1 -0
  46. package/lib/typescript/module/theme/color-scope/index.web.d.ts.map +1 -1
  47. package/package.json +22 -1
  48. package/src/__tests__/BloomColorScope.test.tsx +67 -0
  49. package/src/__tests__/scroll-native.test.ts +25 -0
  50. package/src/__tests__/scroll-store.test.ts +85 -0
  51. package/src/scroll/index.ts +47 -0
  52. package/src/scroll/index.web.tsx +167 -0
  53. package/src/scroll/scrollable.web.ts +75 -0
  54. package/src/scroll/store.ts +84 -0
  55. package/src/scroll/types.ts +48 -0
  56. package/src/theme/color-scope/index.web.tsx +26 -2
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ScrollRestorationProvider = ScrollRestorationProvider;
7
+ exports.useScrollRestoration = useScrollRestoration;
8
+ /**
9
+ * Native variant of the scroll-restoration primitive — a deliberate no-op.
10
+ *
11
+ * React Navigation's native-stack keeps every screen mounted while it is in the
12
+ * stack, so a list's scroll position survives a push/pop for free. There is
13
+ * nothing to save or restore. We still ship the full API surface (provider +
14
+ * hook) so consumers write one set of call sites that compile and run on every
15
+ * platform; on native the provider just renders its children and the hook does
16
+ * nothing.
17
+ *
18
+ * Web bundlers select `./index.web` via the `"browser"` export condition in
19
+ * `package.json`; native bundlers fall through to this file.
20
+ */
21
+
22
+ /**
23
+ * No-op provider. Renders children unchanged — native scroll persistence is
24
+ * handled by the navigator, so no per-route state is kept.
25
+ */
26
+ function ScrollRestorationProvider({
27
+ children
28
+ }) {
29
+ return children;
30
+ }
31
+
32
+ /**
33
+ * No-op hook. Accepts the same arguments as the web implementation so call
34
+ * sites are identical across platforms.
35
+ */
36
+ function useScrollRestoration(_target, _options) {
37
+ // Intentionally empty: native-stack already preserves scroll position.
38
+ }
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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;AACO,SAASA,yBAAyBA,CAAC;EACxCC;AAC8B,CAAC,EAAgB;EAC/C,OAAOA,QAAQ;AACjB;;AAEA;AACA;AACA;AACA;AACO,SAASC,oBAAoBA,CAClCC,OAAgC,EAChCC,QAAsC,EAChC;EACN;AAAA","ignoreList":[]}
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ScrollRestorationProvider = ScrollRestorationProvider;
7
+ exports.useScrollRestoration = useScrollRestoration;
8
+ var _react = require("react");
9
+ var _native = require("@react-navigation/native");
10
+ var _scrollableWeb = require("./scrollable.web.js");
11
+ var _store = require("./store.js");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ /**
14
+ * Web variant of the scroll-restoration primitive.
15
+ *
16
+ * Mirrors the proven Bluesky pattern (`history.scrollRestoration = 'manual'`
17
+ * plus an in-memory `Map<routeKey, offset>` saved on blur and restored on focus
18
+ * inside a single `requestAnimationFrame`) with one deliberate difference:
19
+ * Bluesky restores the WINDOW scroller, whereas Oxy apps keep multi-column
20
+ * layouts whose feed scrolls an INNER container. So we restore the offset of a
21
+ * caller-registered scrollable (a ref to an element / RN scroll component, or
22
+ * the `'window'` sentinel), keyed by the active navigation route.
23
+ *
24
+ * Native bundlers use `./index.ts` (a no-op); web bundlers select this file via
25
+ * the `"browser"` export condition in `package.json`.
26
+ */
27
+
28
+ const ScrollOffsetContext = /*#__PURE__*/(0, _react.createContext)(null);
29
+ ScrollOffsetContext.displayName = 'BloomScrollOffsetContext';
30
+
31
+ /**
32
+ * Switch the browser to manual scroll restoration exactly once per document.
33
+ *
34
+ * The browser's default `'auto'` restoration fights our manual restore on
35
+ * Back/Forward navigations. Doing this at module scope (guarded for SSR) means
36
+ * it is set before any provider mounts, matching Bluesky's module-level call.
37
+ */
38
+ if (typeof history !== 'undefined' && 'scrollRestoration' in history) {
39
+ history.scrollRestoration = 'manual';
40
+ }
41
+
42
+ /**
43
+ * Holds the per-route offset map for the subtree. One provider near the app
44
+ * root is enough; the store lives for the document's lifetime so offsets
45
+ * survive navigating away and back (including browser Back/Forward).
46
+ */
47
+ function ScrollRestorationProvider({
48
+ children
49
+ }) {
50
+ const store = (0, _react.useMemo)(() => new _store.ScrollOffsetStore(), []);
51
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(ScrollOffsetContext.Provider, {
52
+ value: store,
53
+ children: children
54
+ });
55
+ }
56
+ function useScrollOffsetStore() {
57
+ const store = (0, _react.useContext)(ScrollOffsetContext);
58
+ if (store === null) {
59
+ throw new Error('useScrollRestoration must be used within a <ScrollRestorationProvider>.');
60
+ }
61
+ return store;
62
+ }
63
+
64
+ /**
65
+ * Preserve and restore the scroll offset of `target` across navigation, keyed
66
+ * by the active route (plus an optional `options.key` for routes that host
67
+ * multiple scrollables).
68
+ *
69
+ * Behaviour (web):
70
+ * - On every scroll while the screen is focused, the current offset is saved.
71
+ * - On focus, the saved offset is applied in a single `requestAnimationFrame`,
72
+ * giving a remounted list one frame to lay out its content first (no retry
73
+ * loops, no hide/show tricks).
74
+ * - On blur, the latest offset is captured as a final safety net.
75
+ */
76
+ function useScrollRestoration(target, options) {
77
+ const store = useScrollOffsetStore();
78
+ const route = (0, _native.useRoute)();
79
+ const subKey = options?.key;
80
+ const enabled = options?.enabled ?? true;
81
+ const scrollKey = (0, _store.deriveScrollKey)(route.key, subKey);
82
+
83
+ // Keep the latest target/enabled/key in refs so the focus effect can read
84
+ // them without being re-subscribed on every render.
85
+ const targetRef = (0, _react.useRef)(target);
86
+ targetRef.current = target;
87
+ const enabledRef = (0, _react.useRef)(enabled);
88
+ enabledRef.current = enabled;
89
+ const scrollKeyRef = (0, _react.useRef)(scrollKey);
90
+ scrollKeyRef.current = scrollKey;
91
+ (0, _native.useFocusEffect)(
92
+ // The effect identity is intentionally stable across renders: it reads all
93
+ // varying inputs from refs. React Navigation re-runs it on each focus.
94
+ (0, _react.useCallback)(() => {
95
+ const key = scrollKeyRef.current;
96
+ if (!enabledRef.current || key === null) return undefined;
97
+ const scroller = (0, _scrollableWeb.createScroller)(targetRef.current);
98
+ const element = targetRef.current === 'window' ? typeof window !== 'undefined' ? window : null : resolveScrollEventTarget(targetRef.current);
99
+ const save = () => {
100
+ const currentKey = scrollKeyRef.current;
101
+ if (enabledRef.current && currentKey !== null) {
102
+ store.save(currentKey, scroller.getOffset());
103
+ }
104
+ };
105
+
106
+ // Restore on the next frame so a freshly remounted list has rendered
107
+ // its content (and thus reached its full scroll height) before we move.
108
+ const frame = requestAnimationFrame(() => {
109
+ scroller.setOffset(store.read(key));
110
+ });
111
+ element?.addEventListener('scroll', save, {
112
+ passive: true
113
+ });
114
+ return () => {
115
+ cancelAnimationFrame(frame);
116
+ element?.removeEventListener('scroll', save);
117
+ // Final capture on blur, covering navigations that don't fire a
118
+ // trailing scroll event.
119
+ save();
120
+ };
121
+ }, [store]));
122
+ }
123
+
124
+ /**
125
+ * Resolve the EventTarget to listen for `scroll` on. RNW scroll components
126
+ * expose `getScrollableNode()`; raw refs may hold the DOM node directly.
127
+ */
128
+ function resolveScrollEventTarget(target) {
129
+ const current = target.current;
130
+ if (current == null) return null;
131
+ if (typeof EventTarget !== 'undefined' && current instanceof EventTarget) {
132
+ return current;
133
+ }
134
+ const handle = current;
135
+ if (typeof handle.getScrollableNode === 'function') {
136
+ const node = handle.getScrollableNode();
137
+ if (typeof EventTarget !== 'undefined' && node instanceof EventTarget) {
138
+ return node;
139
+ }
140
+ }
141
+ return null;
142
+ }
143
+ //# sourceMappingURL=index.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_native","_scrollableWeb","_store","_jsxRuntime","ScrollOffsetContext","createContext","displayName","history","scrollRestoration","ScrollRestorationProvider","children","store","useMemo","ScrollOffsetStore","jsx","Provider","value","useScrollOffsetStore","useContext","Error","useScrollRestoration","target","options","route","useRoute","subKey","key","enabled","scrollKey","deriveScrollKey","targetRef","useRef","current","enabledRef","scrollKeyRef","useFocusEffect","useCallback","undefined","scroller","createScroller","element","window","resolveScrollEventTarget","save","currentKey","getOffset","frame","requestAnimationFrame","setOffset","read","addEventListener","passive","cancelAnimationFrame","removeEventListener","EventTarget","handle","getScrollableNode","node"],"sourceRoot":"../../../src","sources":["scroll/index.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createScroller = createScroller;
7
+ /**
8
+ * A normalized read/write interface over whatever the caller registered, so
9
+ * the hook does not branch on target shape. Both methods are no-ops when the
10
+ * underlying element is not yet (or no longer) attached.
11
+ */
12
+
13
+ function isElement(value) {
14
+ return typeof HTMLElement !== 'undefined' && value instanceof HTMLElement;
15
+ }
16
+ function hasGetScrollableNode(value) {
17
+ return typeof value === 'object' && value !== null && typeof value.getScrollableNode === 'function';
18
+ }
19
+
20
+ /**
21
+ * Resolve the live DOM element a target currently points at, or `null`.
22
+ *
23
+ * RNW scroll components expose `getScrollableNode()`; plain refs may hold the
24
+ * DOM node directly. We never cache the node because RNW can swap it across
25
+ * renders — we re-resolve on every read/write.
26
+ */
27
+ function resolveElement(target) {
28
+ if (target === 'window') return null;
29
+ const current = target.current;
30
+ if (current == null) return null;
31
+ if (isElement(current)) return current;
32
+ if (hasGetScrollableNode(current)) {
33
+ const node = current.getScrollableNode();
34
+ if (isElement(node)) return node;
35
+ }
36
+ return null;
37
+ }
38
+
39
+ /**
40
+ * Build a {@link ResolvedScroller} for a target. The window sentinel reads and
41
+ * writes the document scroller; everything else operates on the resolved
42
+ * element's `scrollTop`.
43
+ */
44
+ function createScroller(target) {
45
+ if (target === 'window') {
46
+ return {
47
+ getOffset: () => typeof window === 'undefined' ? 0 : window.scrollY,
48
+ setOffset: offset => {
49
+ if (typeof window !== 'undefined') window.scrollTo(0, offset);
50
+ }
51
+ };
52
+ }
53
+ return {
54
+ getOffset: () => {
55
+ const element = resolveElement(target);
56
+ return element ? element.scrollTop : 0;
57
+ },
58
+ setOffset: offset => {
59
+ const element = resolveElement(target);
60
+ if (element) element.scrollTop = offset;
61
+ }
62
+ };
63
+ }
64
+ //# sourceMappingURL=scrollable.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["isElement","value","HTMLElement","hasGetScrollableNode","getScrollableNode","resolveElement","target","current","node","createScroller","getOffset","window","scrollY","setOffset","offset","scrollTo","element","scrollTop"],"sourceRoot":"../../../src","sources":["scroll/scrollable.web.ts"],"mappings":";;;;;;AAEA;AACA;AACA;AACA;AACA;;AAMA,SAASA,SAASA,CAACC,KAAc,EAAwB;EACvD,OAAO,OAAOC,WAAW,KAAK,WAAW,IAAID,KAAK,YAAYC,WAAW;AAC3E;AAEA,SAASC,oBAAoBA,CAC3BF,KAAc,EACkD;EAChE,OACE,OAAOA,KAAK,KAAK,QAAQ,IACzBA,KAAK,KAAK,IAAI,IACd,OAAQA,KAAK,CAAsBG,iBAAiB,KAAK,UAAU;AAEvE;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,cAAcA,CAACC,MAA+B,EAAsB;EAC3E,IAAIA,MAAM,KAAK,QAAQ,EAAE,OAAO,IAAI;EAEpC,MAAMC,OAAO,GAAID,MAAM,CAA0BC,OAAO;EACxD,IAAIA,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI;EAEhC,IAAIP,SAAS,CAACO,OAAO,CAAC,EAAE,OAAOA,OAAO;EAEtC,IAAIJ,oBAAoB,CAACI,OAAO,CAAC,EAAE;IACjC,MAAMC,IAAI,GAAGD,OAAO,CAACH,iBAAiB,CAAC,CAAC;IACxC,IAAIJ,SAAS,CAACQ,IAAI,CAAC,EAAE,OAAOA,IAAI;EAClC;EAEA,OAAO,IAAI;AACb;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASC,cAAcA,CAACH,MAA+B,EAAoB;EAChF,IAAIA,MAAM,KAAK,QAAQ,EAAE;IACvB,OAAO;MACLI,SAAS,EAAEA,CAAA,KAAO,OAAOC,MAAM,KAAK,WAAW,GAAG,CAAC,GAAGA,MAAM,CAACC,OAAQ;MACrEC,SAAS,EAAGC,MAAM,IAAK;QACrB,IAAI,OAAOH,MAAM,KAAK,WAAW,EAAEA,MAAM,CAACI,QAAQ,CAAC,CAAC,EAAED,MAAM,CAAC;MAC/D;IACF,CAAC;EACH;EAEA,OAAO;IACLJ,SAAS,EAAEA,CAAA,KAAM;MACf,MAAMM,OAAO,GAAGX,cAAc,CAACC,MAAM,CAAC;MACtC,OAAOU,OAAO,GAAGA,OAAO,CAACC,SAAS,GAAG,CAAC;IACxC,CAAC;IACDJ,SAAS,EAAGC,MAAM,IAAK;MACrB,MAAME,OAAO,GAAGX,cAAc,CAACC,MAAM,CAAC;MACtC,IAAIU,OAAO,EAAEA,OAAO,CAACC,SAAS,GAAGH,MAAM;IACzC;EACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ScrollOffsetStore = void 0;
7
+ exports.deriveScrollKey = deriveScrollKey;
8
+ /**
9
+ * Pure, platform-agnostic core of the scroll-restoration primitive.
10
+ *
11
+ * This file deliberately contains NO React and NO DOM/native imports so the
12
+ * logic can be unit-tested in isolation and shared verbatim by the web and
13
+ * native barrels. The web barrel drives it with real scroll offsets; the
14
+ * native barrel never instantiates it (native-stack already restores scroll).
15
+ */
16
+
17
+ /**
18
+ * Separator between the navigation route key and an optional caller-supplied
19
+ * sub-key. A route may host more than one independently-scrolling list (e.g.
20
+ * a tabbed profile screen), so each list contributes its own sub-key.
21
+ *
22
+ * `\0` (NUL) can never appear in a React Navigation route key or in a
23
+ * developer-authored sub-key, so it is collision-free as a delimiter. It is
24
+ * written as an escape sequence (not a literal byte) so the source stays
25
+ * text-diffable.
26
+ */
27
+ const COMPOSITE_KEY_SEPARATOR = '\0';
28
+
29
+ /**
30
+ * Derive the storage key for a scrollable from its owning route key and an
31
+ * optional caller sub-key.
32
+ *
33
+ * - `routeKey` is React Navigation's stable per-route `route.key`.
34
+ * - `subKey` distinguishes multiple scrollables that share a single route.
35
+ *
36
+ * Returns `null` when there is no route key to anchor against (the scrollable
37
+ * is not inside a navigator), which the caller treats as "do not persist".
38
+ */
39
+ function deriveScrollKey(routeKey, subKey) {
40
+ if (!routeKey) return null;
41
+ if (subKey === undefined || subKey === '') return routeKey;
42
+ return `${routeKey}${COMPOSITE_KEY_SEPARATOR}${subKey}`;
43
+ }
44
+
45
+ /**
46
+ * In-memory map of `scrollKey -> last-known scroll offset`.
47
+ *
48
+ * Mirrors the semantics of Bluesky's `Map<screenKey, scrollY>`: offsets live
49
+ * only for the lifetime of the document/session. We never persist them — a
50
+ * full reload should start at the top — and we never auto-evict, because a
51
+ * route can be revisited via browser Forward/Back long after it blurred.
52
+ */
53
+ class ScrollOffsetStore {
54
+ offsets = new Map();
55
+
56
+ /** Persist the offset for a key. A negative offset is clamped to 0. */
57
+ save(key, offset) {
58
+ this.offsets.set(key, offset > 0 ? offset : 0);
59
+ }
60
+
61
+ /**
62
+ * Read the saved offset for a key. Returns `0` when nothing was saved, so
63
+ * callers can restore unconditionally (an unseen list restores to the top).
64
+ */
65
+ read(key) {
66
+ return this.offsets.get(key) ?? 0;
67
+ }
68
+
69
+ /** Whether an offset was ever saved for this key. */
70
+ has(key) {
71
+ return this.offsets.has(key);
72
+ }
73
+
74
+ /** Drop a saved offset (e.g. when a route is permanently removed). */
75
+ forget(key) {
76
+ this.offsets.delete(key);
77
+ }
78
+
79
+ /** Clear every saved offset. Primarily for tests and full resets. */
80
+ clear() {
81
+ this.offsets.clear();
82
+ }
83
+
84
+ /** Number of distinct keys currently tracked. */
85
+ get size() {
86
+ return this.offsets.size;
87
+ }
88
+ }
89
+ exports.ScrollOffsetStore = ScrollOffsetStore;
90
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["COMPOSITE_KEY_SEPARATOR","deriveScrollKey","routeKey","subKey","undefined","ScrollOffsetStore","offsets","Map","save","key","offset","set","read","get","has","forget","delete","clear","size","exports"],"sourceRoot":"../../../src","sources":["scroll/store.ts"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMA,uBAAuB,GAAG,IAAI;;AAEpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,eAAeA,CAC7BC,QAA4B,EAC5BC,MAAe,EACA;EACf,IAAI,CAACD,QAAQ,EAAE,OAAO,IAAI;EAC1B,IAAIC,MAAM,KAAKC,SAAS,IAAID,MAAM,KAAK,EAAE,EAAE,OAAOD,QAAQ;EAC1D,OAAO,GAAGA,QAAQ,GAAGF,uBAAuB,GAAGG,MAAM,EAAE;AACzD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,iBAAiB,CAAC;EACZC,OAAO,GAAG,IAAIC,GAAG,CAAiB,CAAC;;EAEpD;EACAC,IAAIA,CAACC,GAAW,EAAEC,MAAc,EAAQ;IACtC,IAAI,CAACJ,OAAO,CAACK,GAAG,CAACF,GAAG,EAAEC,MAAM,GAAG,CAAC,GAAGA,MAAM,GAAG,CAAC,CAAC;EAChD;;EAEA;AACF;AACA;AACA;EACEE,IAAIA,CAACH,GAAW,EAAU;IACxB,OAAO,IAAI,CAACH,OAAO,CAACO,GAAG,CAACJ,GAAG,CAAC,IAAI,CAAC;EACnC;;EAEA;EACAK,GAAGA,CAACL,GAAW,EAAW;IACxB,OAAO,IAAI,CAACH,OAAO,CAACQ,GAAG,CAACL,GAAG,CAAC;EAC9B;;EAEA;EACAM,MAAMA,CAACN,GAAW,EAAQ;IACxB,IAAI,CAACH,OAAO,CAACU,MAAM,CAACP,GAAG,CAAC;EAC1B;;EAEA;EACAQ,KAAKA,CAAA,EAAS;IACZ,IAAI,CAACX,OAAO,CAACW,KAAK,CAAC,CAAC;EACtB;;EAEA;EACA,IAAIC,IAAIA,CAAA,EAAW;IACjB,OAAO,IAAI,CAACZ,OAAO,CAACY,IAAI;EAC1B;AACF;AAACC,OAAA,CAAAd,iBAAA,GAAAA,iBAAA","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["scroll/types.ts"],"mappings":"","ignoreList":[]}
@@ -11,6 +11,16 @@ var _buildTheme = require("../build-theme.js");
11
11
  var _styleBuilder = require("./style-builder.js");
12
12
  var _jsxRuntime = require("react/jsx-runtime");
13
13
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ /**
15
+ * On web, the single `asChild` child is frequently a react-native-web
16
+ * component (e.g. RN `<View>`) whose `style` prop is a *style array* (or a
17
+ * numeric registered-style id), not a plain `React.CSSProperties` object.
18
+ * react-native-web flattens nested style arrays, so the cloned child must
19
+ * receive an array — spreading an array into an object literal would copy its
20
+ * numeric indices as keys and crash RNW when it commits them to the DOM
21
+ * (`Failed to set an indexed property [0] on 'CSSStyleDeclaration'`).
22
+ */
23
+
14
24
  function BloomColorScope({
15
25
  colorPreset,
16
26
  asChild = false,
@@ -40,16 +50,19 @@ function BloomColorScope({
40
50
  if (! /*#__PURE__*/(0, _react.isValidElement)(child)) {
41
51
  throw new Error('BloomColorScope with `asChild` requires a single React element child that accepts a `style` prop.');
42
52
  }
53
+ // Merge as a style ARRAY (the RNW-safe form): scope vars first, then the
54
+ // caller's `style`, then the child's own `style` last so its explicit
55
+ // styles win. react-native-web flattens nested arrays correctly; spreading
56
+ // the child's style (which is often an RN style array or numeric id) into an
57
+ // object literal would copy numeric indices as keys and crash RNW.
43
58
  const childStyle = child.props.style;
44
- const mergedStyle = {
45
- ...varsStyle,
46
- ...style,
47
- ...childStyle
48
- };
59
+ const mergedStyle = [varsStyle, style, childStyle];
49
60
  content = /*#__PURE__*/(0, _react.cloneElement)(child, {
50
61
  style: mergedStyle
51
62
  });
52
63
  } else {
64
+ // A plain DOM `<div>` does NOT accept style arrays — only the cloned child
65
+ // path can, so the wrapper keeps using an object spread.
53
66
  const mergedStyle = {
54
67
  ...varsStyle,
55
68
  ...style
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_BloomThemeProvider","_buildTheme","_styleBuilder","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","BloomColorScope","colorPreset","asChild","style","children","parent","useContext","BloomThemeContext","Error","jsx","Fragment","resolvedMode","theme","mode","contextValue","useMemo","buildTheme","varsStyle","buildScopeVars","content","child","Children","only","isValidElement","childStyle","props","mergedStyle","cloneElement","Provider","value","useColorScopeStyle"],"sourceRoot":"../../../../src","sources":["theme/color-scope/index.web.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_BloomThemeProvider","_buildTheme","_styleBuilder","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","BloomColorScope","colorPreset","asChild","style","children","parent","useContext","BloomThemeContext","Error","jsx","Fragment","resolvedMode","theme","mode","contextValue","useMemo","buildTheme","varsStyle","buildScopeVars","content","child","Children","only","isValidElement","childStyle","props","mergedStyle","cloneElement","Provider","value","useColorScopeStyle"],"sourceRoot":"../../../../src","sources":["theme/color-scope/index.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Native variant of the scroll-restoration primitive — a deliberate no-op.
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.
12
+ *
13
+ * Web bundlers select `./index.web` via the `"browser"` export condition in
14
+ * `package.json`; native bundlers fall through to this file.
15
+ */
16
+
17
+ /**
18
+ * No-op provider. Renders children unchanged — native scroll persistence is
19
+ * handled by the navigator, so no per-route state is kept.
20
+ */
21
+ export function ScrollRestorationProvider({
22
+ children
23
+ }) {
24
+ return children;
25
+ }
26
+
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.
33
+ }
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":[]}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Web variant of the scroll-restoration primitive.
5
+ *
6
+ * Mirrors the proven Bluesky pattern (`history.scrollRestoration = 'manual'`
7
+ * plus an in-memory `Map<routeKey, offset>` saved on blur and restored on focus
8
+ * inside a single `requestAnimationFrame`) with one deliberate difference:
9
+ * Bluesky restores the WINDOW scroller, whereas Oxy apps keep multi-column
10
+ * layouts whose feed scrolls an INNER container. So we restore the offset of a
11
+ * caller-registered scrollable (a ref to an element / RN scroll component, or
12
+ * the `'window'` sentinel), keyed by the active navigation route.
13
+ *
14
+ * Native bundlers use `./index.ts` (a no-op); web bundlers select this file via
15
+ * the `"browser"` export condition in `package.json`.
16
+ */
17
+ import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
18
+ import { useFocusEffect, useRoute } from '@react-navigation/native';
19
+ import { createScroller } from "./scrollable.web.js";
20
+ import { ScrollOffsetStore, deriveScrollKey } from "./store.js";
21
+ import { jsx as _jsx } from "react/jsx-runtime";
22
+ const ScrollOffsetContext = /*#__PURE__*/createContext(null);
23
+ ScrollOffsetContext.displayName = 'BloomScrollOffsetContext';
24
+
25
+ /**
26
+ * Switch the browser to manual scroll restoration exactly once per document.
27
+ *
28
+ * The browser's default `'auto'` restoration fights our manual restore on
29
+ * Back/Forward navigations. Doing this at module scope (guarded for SSR) means
30
+ * it is set before any provider mounts, matching Bluesky's module-level call.
31
+ */
32
+ if (typeof history !== 'undefined' && 'scrollRestoration' in history) {
33
+ history.scrollRestoration = 'manual';
34
+ }
35
+
36
+ /**
37
+ * Holds the per-route offset map for the subtree. One provider near the app
38
+ * root is enough; the store lives for the document's lifetime so offsets
39
+ * survive navigating away and back (including browser Back/Forward).
40
+ */
41
+ export function ScrollRestorationProvider({
42
+ children
43
+ }) {
44
+ const store = useMemo(() => new ScrollOffsetStore(), []);
45
+ return /*#__PURE__*/_jsx(ScrollOffsetContext.Provider, {
46
+ value: store,
47
+ children: children
48
+ });
49
+ }
50
+ function useScrollOffsetStore() {
51
+ const store = useContext(ScrollOffsetContext);
52
+ if (store === null) {
53
+ throw new Error('useScrollRestoration must be used within a <ScrollRestorationProvider>.');
54
+ }
55
+ return store;
56
+ }
57
+
58
+ /**
59
+ * Preserve and restore the scroll offset of `target` across navigation, keyed
60
+ * by the active route (plus an optional `options.key` for routes that host
61
+ * multiple scrollables).
62
+ *
63
+ * Behaviour (web):
64
+ * - On every scroll while the screen is focused, the current offset is saved.
65
+ * - On focus, the saved offset is applied in a single `requestAnimationFrame`,
66
+ * giving a remounted list one frame to lay out its content first (no retry
67
+ * loops, no hide/show tricks).
68
+ * - On blur, the latest offset is captured as a final safety net.
69
+ */
70
+ export function useScrollRestoration(target, options) {
71
+ const store = useScrollOffsetStore();
72
+ const route = useRoute();
73
+ const subKey = options?.key;
74
+ const enabled = options?.enabled ?? true;
75
+ const scrollKey = deriveScrollKey(route.key, subKey);
76
+
77
+ // Keep the latest target/enabled/key in refs so the focus effect can read
78
+ // them without being re-subscribed on every render.
79
+ const targetRef = useRef(target);
80
+ targetRef.current = target;
81
+ const enabledRef = useRef(enabled);
82
+ enabledRef.current = enabled;
83
+ const scrollKeyRef = useRef(scrollKey);
84
+ scrollKeyRef.current = scrollKey;
85
+ useFocusEffect(
86
+ // The effect identity is intentionally stable across renders: it reads all
87
+ // varying inputs from refs. React Navigation re-runs it on each focus.
88
+ useCallback(() => {
89
+ const key = scrollKeyRef.current;
90
+ if (!enabledRef.current || key === null) return undefined;
91
+ const scroller = createScroller(targetRef.current);
92
+ const element = targetRef.current === 'window' ? typeof window !== 'undefined' ? window : null : resolveScrollEventTarget(targetRef.current);
93
+ const save = () => {
94
+ const currentKey = scrollKeyRef.current;
95
+ if (enabledRef.current && currentKey !== null) {
96
+ store.save(currentKey, scroller.getOffset());
97
+ }
98
+ };
99
+
100
+ // Restore on the next frame so a freshly remounted list has rendered
101
+ // its content (and thus reached its full scroll height) before we move.
102
+ const frame = requestAnimationFrame(() => {
103
+ scroller.setOffset(store.read(key));
104
+ });
105
+ element?.addEventListener('scroll', save, {
106
+ passive: true
107
+ });
108
+ return () => {
109
+ cancelAnimationFrame(frame);
110
+ element?.removeEventListener('scroll', save);
111
+ // Final capture on blur, covering navigations that don't fire a
112
+ // trailing scroll event.
113
+ save();
114
+ };
115
+ }, [store]));
116
+ }
117
+
118
+ /**
119
+ * Resolve the EventTarget to listen for `scroll` on. RNW scroll components
120
+ * expose `getScrollableNode()`; raw refs may hold the DOM node directly.
121
+ */
122
+ function resolveScrollEventTarget(target) {
123
+ const current = target.current;
124
+ if (current == null) return null;
125
+ if (typeof EventTarget !== 'undefined' && current instanceof EventTarget) {
126
+ return current;
127
+ }
128
+ const handle = current;
129
+ if (typeof handle.getScrollableNode === 'function') {
130
+ const node = handle.getScrollableNode();
131
+ if (typeof EventTarget !== 'undefined' && node instanceof EventTarget) {
132
+ return node;
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ //# sourceMappingURL=index.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createContext","useCallback","useContext","useMemo","useRef","useFocusEffect","useRoute","createScroller","ScrollOffsetStore","deriveScrollKey","jsx","_jsx","ScrollOffsetContext","displayName","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","save","currentKey","getOffset","frame","requestAnimationFrame","setOffset","read","addEventListener","passive","cancelAnimationFrame","removeEventListener","EventTarget","handle","getScrollableNode","node"],"sourceRoot":"../../../src","sources":["scroll/index.web.tsx"],"mappings":"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","ignoreList":[]}