@oxyhq/bloom 0.24.0 → 0.25.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 (43) hide show
  1. package/lib/commonjs/link-preview/LinkPreviewCard.js +9 -1
  2. package/lib/commonjs/link-preview/LinkPreviewCard.js.map +1 -1
  3. package/lib/commonjs/list/index.js +94 -0
  4. package/lib/commonjs/list/index.js.map +1 -0
  5. package/lib/commonjs/list/index.web.js +216 -0
  6. package/lib/commonjs/list/index.web.js.map +1 -0
  7. package/lib/commonjs/list/types.js +6 -0
  8. package/lib/commonjs/list/types.js.map +1 -0
  9. package/lib/module/link-preview/LinkPreviewCard.js +9 -1
  10. package/lib/module/link-preview/LinkPreviewCard.js.map +1 -1
  11. package/lib/module/list/index.js +89 -0
  12. package/lib/module/list/index.js.map +1 -0
  13. package/lib/module/list/index.web.js +211 -0
  14. package/lib/module/list/index.web.js.map +1 -0
  15. package/lib/module/list/types.js +4 -0
  16. package/lib/module/list/types.js.map +1 -0
  17. package/lib/typescript/commonjs/link-preview/LinkPreviewCard.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/link-preview/types.d.ts +12 -0
  19. package/lib/typescript/commonjs/link-preview/types.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/list/index.d.ts +29 -0
  21. package/lib/typescript/commonjs/list/index.d.ts.map +1 -0
  22. package/lib/typescript/commonjs/list/index.web.d.ts +45 -0
  23. package/lib/typescript/commonjs/list/index.web.d.ts.map +1 -0
  24. package/lib/typescript/commonjs/list/types.d.ts +77 -0
  25. package/lib/typescript/commonjs/list/types.d.ts.map +1 -0
  26. package/lib/typescript/module/link-preview/LinkPreviewCard.d.ts.map +1 -1
  27. package/lib/typescript/module/link-preview/types.d.ts +12 -0
  28. package/lib/typescript/module/link-preview/types.d.ts.map +1 -1
  29. package/lib/typescript/module/list/index.d.ts +29 -0
  30. package/lib/typescript/module/list/index.d.ts.map +1 -0
  31. package/lib/typescript/module/list/index.web.d.ts +45 -0
  32. package/lib/typescript/module/list/index.web.d.ts.map +1 -0
  33. package/lib/typescript/module/list/types.d.ts +77 -0
  34. package/lib/typescript/module/list/types.d.ts.map +1 -0
  35. package/package.json +20 -1
  36. package/src/__tests__/LinkPreviewCard.test.tsx +29 -1
  37. package/src/__tests__/VirtualList.native.test.tsx +67 -0
  38. package/src/__tests__/VirtualList.web.test.tsx +127 -0
  39. package/src/link-preview/LinkPreviewCard.tsx +5 -1
  40. package/src/link-preview/types.ts +12 -0
  41. package/src/list/index.tsx +121 -0
  42. package/src/list/index.web.tsx +261 -0
  43. package/src/list/types.ts +85 -0
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * `VirtualList` — WEB variant. A document-scroll (window) virtualizer built on
5
+ * `@tanstack/react-virtual`'s `useWindowVirtualizer`, mirroring the proven
6
+ * Mention feed virtualizer. The BODY scrolls (so scrolling works from anywhere,
7
+ * including over sticky side columns) and only the rows in the virtual window
8
+ * are mounted, so the DOM stays bounded.
9
+ *
10
+ * Why this exists (the bug it fixes): a previous app-local list used the same
11
+ * hook but rendered ZERO rows in production — the wrapper grew to the right
12
+ * height (`getTotalSize()` was correct) yet `getVirtualItems()` came back empty,
13
+ * so only the header painted. Root cause: `useWindowVirtualizer` can latch an
14
+ * initial frame in which the window rect measured as `0` (or `scrollMargin` was
15
+ * still stale), which leaves the computed range empty; a STATIC, finite list
16
+ * re-renders too few times to self-correct and sticks on that bad frame. (The
17
+ * feed only survived by incidental re-render churn — data/loading updates and
18
+ * scroll/intersection observers — which a plain list does not have.)
19
+ *
20
+ * This implementation is SELF-SUFFICIENT and does not rely on incidental churn:
21
+ *
22
+ * 1. `scrollMargin` (the wrapper's offset from the document top) is measured in
23
+ * a layout effect AND re-synced on window `resize`.
24
+ * 2. An explicit post-mount recompute (`virtualizer.measure()`) forces the
25
+ * range to be recomputed once the real viewport height + offset are known —
26
+ * guaranteeing a short list mounts EVERY row in the first viewport even with
27
+ * zero further re-renders. Re-run on resize.
28
+ * 3. The measured spacer is `Math.max(totalSize, lastItemEnd)`, so it always
29
+ * contains every absolutely-positioned row even when `scrollMargin` is
30
+ * momentarily stale; the document therefore always grows to the full content
31
+ * height (keeping sticky side rails pinned) and the window-scroll geometry
32
+ * the virtualizer reads stays consistent.
33
+ * 4. Per-row `measureElement` refs are STABLE (cached per key), so rows are not
34
+ * detached/re-measured on every render.
35
+ *
36
+ * Native bundlers use `./index.tsx` (a `FlatList` wrapper); web bundlers select
37
+ * this file via the `"browser"` export condition in `package.json`.
38
+ */
39
+ import * as React from 'react';
40
+ import { StyleSheet, View } from 'react-native';
41
+ import { useWindowVirtualizer } from '@tanstack/react-virtual';
42
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
43
+ // Rows in these lists (user rows, pack cards) are short and fairly uniform; a
44
+ // small estimate + per-row measurement keeps the mounted node count bounded.
45
+ const DEFAULT_ESTIMATED_ITEM_SIZE = 72;
46
+ const DEFAULT_OVERSCAN = 8;
47
+
48
+ // Bound the per-key ref-callback cache so a very long scroll session does not
49
+ // accumulate one closure per key forever.
50
+ const ROW_REF_CACHE_CAP = 500;
51
+ function renderSlot(slot) {
52
+ if (!slot) return null;
53
+ return typeof slot === 'function' ? slot() : slot;
54
+ }
55
+ function VirtualListWebInner(props, ref) {
56
+ const {
57
+ data,
58
+ renderItem,
59
+ keyExtractor,
60
+ ListHeaderComponent,
61
+ ListEmptyComponent,
62
+ ListFooterComponent,
63
+ style,
64
+ contentContainerStyle,
65
+ estimatedItemSize,
66
+ overscan,
67
+ testID
68
+ } = props;
69
+ const items = data ?? [];
70
+ const count = items.length;
71
+
72
+ // Wrapper element used as the virtualizer's measurement origin. The window is
73
+ // the scroller; `scrollMargin` is the wrapper's offset from the document top
74
+ // (everything above the rows), so virtual offsets map to page offsets.
75
+ const wrapperRef = React.useRef(null);
76
+ const [scrollMargin, setScrollMargin] = React.useState(0);
77
+ const measureScrollMargin = React.useCallback(() => {
78
+ const node = wrapperRef.current;
79
+ if (!node || typeof window === 'undefined') return;
80
+ const top = node.getBoundingClientRect().top + window.scrollY;
81
+ setScrollMargin(prev => prev !== top ? top : prev);
82
+ }, []);
83
+
84
+ // Read the wrapper's top offset synchronously after layout so the first
85
+ // virtual frame already positions rows correctly. Re-run when the row count
86
+ // changes (content above/below the window shifts the wrapper).
87
+ React.useLayoutEffect(() => {
88
+ measureScrollMargin();
89
+ }, [measureScrollMargin, count]);
90
+ const estimate = estimatedItemSize ?? DEFAULT_ESTIMATED_ITEM_SIZE;
91
+ const virtualizer = useWindowVirtualizer({
92
+ count,
93
+ estimateSize: () => estimate,
94
+ overscan: overscan ?? DEFAULT_OVERSCAN,
95
+ scrollMargin,
96
+ getItemKey: keyExtractor ? index => {
97
+ const item = items[index];
98
+ return item !== undefined ? keyExtractor(item, index) : index;
99
+ } : undefined
100
+ });
101
+
102
+ // Explicit post-mount recompute + resize re-sync. `useWindowVirtualizer` can
103
+ // latch a frame where the window rect read as 0 / `scrollMargin` was stale,
104
+ // leaving `getVirtualItems()` empty even though rows exist. A static list does
105
+ // not re-render enough to self-correct, so we force the range to recompute
106
+ // once the real viewport + offset are known — and again on every resize.
107
+ React.useEffect(() => {
108
+ if (typeof window === 'undefined') return;
109
+ const sync = () => {
110
+ measureScrollMargin();
111
+ virtualizer.measure();
112
+ };
113
+ sync();
114
+ window.addEventListener('resize', sync);
115
+ return () => window.removeEventListener('resize', sync);
116
+ }, [measureScrollMargin, virtualizer]);
117
+ React.useImperativeHandle(ref, () => ({
118
+ scrollToOffset: params => {
119
+ if (typeof window === 'undefined') return;
120
+ window.scrollTo({
121
+ top: params?.offset ?? 0,
122
+ behavior: params?.animated === false ? 'auto' : 'smooth'
123
+ });
124
+ },
125
+ scrollTo: params => {
126
+ if (typeof window === 'undefined') return;
127
+ window.scrollTo({
128
+ top: params?.y ?? 0,
129
+ behavior: params?.animated === false ? 'auto' : 'smooth'
130
+ });
131
+ }
132
+ }), []);
133
+
134
+ // Stable per-key ref factory wiring the virtualizer's measurement ref. A fresh
135
+ // inline arrow each render would make React detach/re-attach (and re-measure)
136
+ // every row on every render; the cached callback is reused for the same key so
137
+ // the ref only fires on real mount/unmount.
138
+ const measureElement = virtualizer.measureElement;
139
+ const rowRefCallbacks = React.useRef(new Map());
140
+ const getRowRef = React.useCallback(key => {
141
+ const cache = rowRefCallbacks.current;
142
+ const existing = cache.get(key);
143
+ if (existing) return existing;
144
+ if (cache.size > ROW_REF_CACHE_CAP) cache.clear();
145
+ const cb = node => measureElement(node);
146
+ cache.set(key, cb);
147
+ return cb;
148
+ }, [measureElement]);
149
+ const flatStyle = StyleSheet.flatten(style);
150
+ const flatContentStyle = StyleSheet.flatten(contentContainerStyle);
151
+ const header = renderSlot(ListHeaderComponent);
152
+ const footer = renderSlot(ListFooterComponent);
153
+ if (count === 0) {
154
+ return /*#__PURE__*/_jsxs(View, {
155
+ style: [{
156
+ minHeight: 0
157
+ }, flatStyle],
158
+ testID: testID,
159
+ children: [header, renderSlot(ListEmptyComponent), footer]
160
+ });
161
+ }
162
+ const virtualItems = virtualizer.getVirtualItems();
163
+ const totalSize = virtualizer.getTotalSize();
164
+
165
+ // The measured spacer MUST contain every absolutely-positioned row; size it to
166
+ // the MAX of `totalSize` and the rows' real extent so the document always
167
+ // grows to full content height even when `scrollMargin` is momentarily stale.
168
+ const lastItem = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1] : undefined;
169
+ const lastItemEnd = lastItem ? lastItem.start + lastItem.size - virtualizer.options.scrollMargin : 0;
170
+ const spacerHeight = Math.max(totalSize, lastItemEnd);
171
+ return /*#__PURE__*/_jsxs(View, {
172
+ style: [{
173
+ minHeight: 0
174
+ }, flatStyle],
175
+ testID: testID,
176
+ children: [header, /*#__PURE__*/_jsx("div", {
177
+ style: flatContentStyle,
178
+ children: /*#__PURE__*/_jsx("div", {
179
+ ref: wrapperRef,
180
+ style: {
181
+ height: spacerHeight,
182
+ width: '100%',
183
+ position: 'relative'
184
+ },
185
+ children: virtualItems.map(virtualRow => {
186
+ const item = items[virtualRow.index];
187
+ if (item === undefined) return null;
188
+ return /*#__PURE__*/_jsx("div", {
189
+ ref: getRowRef(String(virtualRow.key)),
190
+ "data-index": virtualRow.index,
191
+ style: {
192
+ position: 'absolute',
193
+ top: 0,
194
+ left: 0,
195
+ width: '100%',
196
+ transform: `translateY(${virtualRow.start - virtualizer.options.scrollMargin}px)`
197
+ },
198
+ children: renderItem ? renderItem({
199
+ item,
200
+ index: virtualRow.index
201
+ }) : null
202
+ }, virtualRow.key);
203
+ })
204
+ })
205
+ }), footer]
206
+ });
207
+ }
208
+ const VirtualList = /*#__PURE__*/React.forwardRef(VirtualListWebInner);
209
+ export { VirtualList };
210
+ export default VirtualList;
211
+ //# sourceMappingURL=index.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","StyleSheet","View","useWindowVirtualizer","jsxs","_jsxs","jsx","_jsx","DEFAULT_ESTIMATED_ITEM_SIZE","DEFAULT_OVERSCAN","ROW_REF_CACHE_CAP","renderSlot","slot","VirtualListWebInner","props","ref","data","renderItem","keyExtractor","ListHeaderComponent","ListEmptyComponent","ListFooterComponent","style","contentContainerStyle","estimatedItemSize","overscan","testID","items","count","length","wrapperRef","useRef","scrollMargin","setScrollMargin","useState","measureScrollMargin","useCallback","node","current","window","top","getBoundingClientRect","scrollY","prev","useLayoutEffect","estimate","virtualizer","estimateSize","getItemKey","index","item","undefined","useEffect","sync","measure","addEventListener","removeEventListener","useImperativeHandle","scrollToOffset","params","scrollTo","offset","behavior","animated","y","measureElement","rowRefCallbacks","Map","getRowRef","key","cache","existing","get","size","clear","cb","set","flatStyle","flatten","flatContentStyle","header","footer","minHeight","children","virtualItems","getVirtualItems","totalSize","getTotalSize","lastItem","lastItemEnd","start","options","spacerHeight","Math","max","height","width","position","map","virtualRow","String","left","transform","VirtualList","forwardRef"],"sourceRoot":"../../../src","sources":["list/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,OAAO,KAAKA,KAAK,MAAM,OAAO;AAE9B,SAASC,UAAU,EAAEC,IAAI,QAAQ,cAAc;AAC/C,SAASC,oBAAoB,QAAQ,yBAAyB;AAAC,SAAAC,IAAA,IAAAC,KAAA,EAAAC,GAAA,IAAAC,IAAA;AAgB/D;AACA;AACA,MAAMC,2BAA2B,GAAG,EAAE;AACtC,MAAMC,gBAAgB,GAAG,CAAC;;AAE1B;AACA;AACA,MAAMC,iBAAiB,GAAG,GAAG;AAE7B,SAASC,UAAUA,CAACC,IAAqB,EAA6B;EACpE,IAAI,CAACA,IAAI,EAAE,OAAO,IAAI;EACtB,OAAO,OAAOA,IAAI,KAAK,UAAU,GAAGA,IAAI,CAAC,CAAC,GAAGA,IAAI;AACnD;AAEA,SAASC,mBAAmBA,CAC1BC,KAA0B,EAC1BC,GAA0C,EAC1C;EACA,MAAM;IACJC,IAAI;IACJC,UAAU;IACVC,YAAY;IACZC,mBAAmB;IACnBC,kBAAkB;IAClBC,mBAAmB;IACnBC,KAAK;IACLC,qBAAqB;IACrBC,iBAAiB;IACjBC,QAAQ;IACRC;EACF,CAAC,GAAGZ,KAAK;EAET,MAAMa,KAAK,GAAGX,IAAI,IAAI,EAAE;EACxB,MAAMY,KAAK,GAAGD,KAAK,CAACE,MAAM;;EAE1B;EACA;EACA;EACA,MAAMC,UAAU,GAAG9B,KAAK,CAAC+B,MAAM,CAAwB,IAAI,CAAC;EAC5D,MAAM,CAACC,YAAY,EAAEC,eAAe,CAAC,GAAGjC,KAAK,CAACkC,QAAQ,CAAC,CAAC,CAAC;EAEzD,MAAMC,mBAAmB,GAAGnC,KAAK,CAACoC,WAAW,CAAC,MAAM;IAClD,MAAMC,IAAI,GAAGP,UAAU,CAACQ,OAAO;IAC/B,IAAI,CAACD,IAAI,IAAI,OAAOE,MAAM,KAAK,WAAW,EAAE;IAC5C,MAAMC,GAAG,GAAGH,IAAI,CAACI,qBAAqB,CAAC,CAAC,CAACD,GAAG,GAAGD,MAAM,CAACG,OAAO;IAC7DT,eAAe,CAAEU,IAAI,IAAMA,IAAI,KAAKH,GAAG,GAAGA,GAAG,GAAGG,IAAK,CAAC;EACxD,CAAC,EAAE,EAAE,CAAC;;EAEN;EACA;EACA;EACA3C,KAAK,CAAC4C,eAAe,CAAC,MAAM;IAC1BT,mBAAmB,CAAC,CAAC;EACvB,CAAC,EAAE,CAACA,mBAAmB,EAAEP,KAAK,CAAC,CAAC;EAEhC,MAAMiB,QAAQ,GAAGrB,iBAAiB,IAAIhB,2BAA2B;EAEjE,MAAMsC,WAAW,GAAG3C,oBAAoB,CAAiB;IACvDyB,KAAK;IACLmB,YAAY,EAAEA,CAAA,KAAMF,QAAQ;IAC5BpB,QAAQ,EAAEA,QAAQ,IAAIhB,gBAAgB;IACtCuB,YAAY;IACZgB,UAAU,EAAE9B,YAAY,GACnB+B,KAAK,IAAK;MACT,MAAMC,IAAI,GAAGvB,KAAK,CAACsB,KAAK,CAAC;MACzB,OAAOC,IAAI,KAAKC,SAAS,GAAGjC,YAAY,CAACgC,IAAI,EAAED,KAAK,CAAC,GAAGA,KAAK;IAC/D,CAAC,GACDE;EACN,CAAC,CAAC;;EAEF;EACA;EACA;EACA;EACA;EACAnD,KAAK,CAACoD,SAAS,CAAC,MAAM;IACpB,IAAI,OAAOb,MAAM,KAAK,WAAW,EAAE;IACnC,MAAMc,IAAI,GAAGA,CAAA,KAAM;MACjBlB,mBAAmB,CAAC,CAAC;MACrBW,WAAW,CAACQ,OAAO,CAAC,CAAC;IACvB,CAAC;IACDD,IAAI,CAAC,CAAC;IACNd,MAAM,CAACgB,gBAAgB,CAAC,QAAQ,EAAEF,IAAI,CAAC;IACvC,OAAO,MAAMd,MAAM,CAACiB,mBAAmB,CAAC,QAAQ,EAAEH,IAAI,CAAC;EACzD,CAAC,EAAE,CAAClB,mBAAmB,EAAEW,WAAW,CAAC,CAAC;EAEtC9C,KAAK,CAACyD,mBAAmB,CACvB1C,GAAG,EACH,OAAO;IACL2C,cAAc,EAAGC,MAAM,IAAK;MAC1B,IAAI,OAAOpB,MAAM,KAAK,WAAW,EAAE;MACnCA,MAAM,CAACqB,QAAQ,CAAC;QACdpB,GAAG,EAAEmB,MAAM,EAAEE,MAAM,IAAI,CAAC;QACxBC,QAAQ,EAAEH,MAAM,EAAEI,QAAQ,KAAK,KAAK,GAAG,MAAM,GAAG;MAClD,CAAC,CAAC;IACJ,CAAC;IACDH,QAAQ,EAAGD,MAAM,IAAK;MACpB,IAAI,OAAOpB,MAAM,KAAK,WAAW,EAAE;MACnCA,MAAM,CAACqB,QAAQ,CAAC;QACdpB,GAAG,EAAEmB,MAAM,EAAEK,CAAC,IAAI,CAAC;QACnBF,QAAQ,EAAEH,MAAM,EAAEI,QAAQ,KAAK,KAAK,GAAG,MAAM,GAAG;MAClD,CAAC,CAAC;IACJ;EACF,CAAC,CAAC,EACF,EACF,CAAC;;EAED;EACA;EACA;EACA;EACA,MAAME,cAAc,GAAGnB,WAAW,CAACmB,cAAc;EACjD,MAAMC,eAAe,GAAGlE,KAAK,CAAC+B,MAAM,CAClC,IAAIoC,GAAG,CAAgD,CACzD,CAAC;EACD,MAAMC,SAAS,GAAGpE,KAAK,CAACoC,WAAW,CAChCiC,GAAW,IAAK;IACf,MAAMC,KAAK,GAAGJ,eAAe,CAAC5B,OAAO;IACrC,MAAMiC,QAAQ,GAAGD,KAAK,CAACE,GAAG,CAACH,GAAG,CAAC;IAC/B,IAAIE,QAAQ,EAAE,OAAOA,QAAQ;IAC7B,IAAID,KAAK,CAACG,IAAI,GAAG/D,iBAAiB,EAAE4D,KAAK,CAACI,KAAK,CAAC,CAAC;IACjD,MAAMC,EAAE,GAAItC,IAA2B,IAAK4B,cAAc,CAAC5B,IAAI,CAAC;IAChEiC,KAAK,CAACM,GAAG,CAACP,GAAG,EAAEM,EAAE,CAAC;IAClB,OAAOA,EAAE;EACX,CAAC,EACD,CAACV,cAAc,CACjB,CAAC;EAED,MAAMY,SAAS,GAAG5E,UAAU,CAAC6E,OAAO,CAACxD,KAAK,CAAC;EAC3C,MAAMyD,gBAAgB,GAAG9E,UAAU,CAAC6E,OAAO,CAACvD,qBAAqB,CAEpD;EAEb,MAAMyD,MAAM,GAAGrE,UAAU,CAACQ,mBAAmB,CAAC;EAC9C,MAAM8D,MAAM,GAAGtE,UAAU,CAACU,mBAAmB,CAAC;EAE9C,IAAIO,KAAK,KAAK,CAAC,EAAE;IACf,oBACEvB,KAAA,CAACH,IAAI;MAACoB,KAAK,EAAE,CAAC;QAAE4D,SAAS,EAAE;MAAE,CAAC,EAAEL,SAAS,CAAE;MAACnD,MAAM,EAAEA,MAAO;MAAAyD,QAAA,GACxDH,MAAM,EACNrE,UAAU,CAACS,kBAAkB,CAAC,EAC9B6D,MAAM;IAAA,CACH,CAAC;EAEX;EAEA,MAAMG,YAAY,GAAGtC,WAAW,CAACuC,eAAe,CAAC,CAAC;EAClD,MAAMC,SAAS,GAAGxC,WAAW,CAACyC,YAAY,CAAC,CAAC;;EAE5C;EACA;EACA;EACA,MAAMC,QAAQ,GACZJ,YAAY,CAACvD,MAAM,GAAG,CAAC,GACnBuD,YAAY,CAACA,YAAY,CAACvD,MAAM,GAAG,CAAC,CAAC,GACrCsB,SAAS;EACf,MAAMsC,WAAW,GAAGD,QAAQ,GACxBA,QAAQ,CAACE,KAAK,GAAGF,QAAQ,CAACf,IAAI,GAAG3B,WAAW,CAAC6C,OAAO,CAAC3D,YAAY,GACjE,CAAC;EACL,MAAM4D,YAAY,GAAGC,IAAI,CAACC,GAAG,CAACR,SAAS,EAAEG,WAAW,CAAC;EAErD,oBACEpF,KAAA,CAACH,IAAI;IAACoB,KAAK,EAAE,CAAC;MAAE4D,SAAS,EAAE;IAAE,CAAC,EAAEL,SAAS,CAAE;IAACnD,MAAM,EAAEA,MAAO;IAAAyD,QAAA,GACxDH,MAAM,eACPzE,IAAA;MAAKe,KAAK,EAAEyD,gBAAiB;MAAAI,QAAA,eAC3B5E,IAAA;QACEQ,GAAG,EAAEe,UAAW;QAChBR,KAAK,EAAE;UAAEyE,MAAM,EAAEH,YAAY;UAAEI,KAAK,EAAE,MAAM;UAAEC,QAAQ,EAAE;QAAW,CAAE;QAAAd,QAAA,EAEpEC,YAAY,CAACc,GAAG,CAAEC,UAAU,IAAK;UAChC,MAAMjD,IAAI,GAAGvB,KAAK,CAACwE,UAAU,CAAClD,KAAK,CAAC;UACpC,IAAIC,IAAI,KAAKC,SAAS,EAAE,OAAO,IAAI;UACnC,oBACE5C,IAAA;YAEEQ,GAAG,EAAEqD,SAAS,CAACgC,MAAM,CAACD,UAAU,CAAC9B,GAAG,CAAC,CAAE;YACvC,cAAY8B,UAAU,CAAClD,KAAM;YAC7B3B,KAAK,EAAE;cACL2E,QAAQ,EAAE,UAAU;cACpBzD,GAAG,EAAE,CAAC;cACN6D,IAAI,EAAE,CAAC;cACPL,KAAK,EAAE,MAAM;cACbM,SAAS,EAAE,cACTH,UAAU,CAACT,KAAK,GAAG5C,WAAW,CAAC6C,OAAO,CAAC3D,YAAY;YAEvD,CAAE;YAAAmD,QAAA,EAEDlE,UAAU,GACPA,UAAU,CAAC;cAAEiC,IAAI;cAAED,KAAK,EAAEkD,UAAU,CAAClD;YAAM,CAAC,CAAC,GAC7C;UAAI,GAfHkD,UAAU,CAAC9B,GAgBb,CAAC;QAEV,CAAC;MAAC,CACC;IAAC,CACH,CAAC,EACLY,MAAM;EAAA,CACH,CAAC;AAEX;AAEA,MAAMsB,WAAW,gBAAGvG,KAAK,CAACwG,UAAU,CAAC3F,mBAAmB,CAEjC;AAEvB,SAAS0F,WAAW;AACpB,eAAeA,WAAW","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["list/types.ts"],"mappings":"","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"LinkPreviewCard.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/LinkPreviewCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAqC,MAAM,OAAO,CAAC;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA0HpD,eAAO,MAAM,eAAe,kDAAiC,CAAC"}
1
+ {"version":3,"file":"LinkPreviewCard.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/LinkPreviewCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAqC,MAAM,OAAO,CAAC;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA8HpD,eAAO,MAAM,eAAe,kDAAiC,CAAC"}
@@ -22,6 +22,18 @@ export interface LinkPreviewCardProps {
22
22
  image?: string;
23
23
  /** Site / brand name shown above the title. Falls back to the URL hostname. */
24
24
  siteName?: string;
25
+ /**
26
+ * Fill-height mode for the cover image. Defaults to `false`.
27
+ *
28
+ * - `false` (default): the cover image keeps its fixed intrinsic height —
29
+ * correct when the card sizes itself (e.g. a link rendered alone).
30
+ * - `true`: the cover image flexes (`flex: 1`, no fixed height) to fill
31
+ * whatever vertical space the card is given, with the text block as a
32
+ * compact intrinsic-height footer below it. The consumer supplies the
33
+ * bounding height via `style` (e.g. `style={{ height: 180 }}`) so the card
34
+ * fits a fixed-height row alongside other attachments without overflowing.
35
+ */
36
+ coverFill?: boolean;
25
37
  /** Press handler. When omitted, the card opens `url` via `Linking.openURL`. */
26
38
  onPress?: () => void;
27
39
  /** Extra NativeWind utilities merged onto the card surface. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `VirtualList` — the canonical cross-platform virtualized list for the Oxy
3
+ * ecosystem. Every app uses this ONE implementation for grouped/stacked row
4
+ * lists (who-to-follow, starter packs, connections, settings rows, …) so the
5
+ * virtualization behaviour is identical everywhere.
6
+ *
7
+ * NATIVE variant: a thin, correctly-typed wrapper over React Native `FlatList`.
8
+ * The consumer lists this component replaces are short and uniform, so the
9
+ * platform's own virtualization is the simplest robust choice — no
10
+ * `@legendapp/list`, no app-specific scroll bridge, nothing for a consumer to
11
+ * wire up. Bloom stays app-agnostic.
12
+ *
13
+ * WEB variant (`index.web.tsx`, selected via the `"browser"` export condition):
14
+ * a document-scroll window virtualizer built on `@tanstack/react-virtual`.
15
+ *
16
+ * This default file has NO platform-only imports, so consumer `tsc` and web
17
+ * bundlers resolve it cleanly (mirrors the `../scroll` / `../content-panel`
18
+ * fork pattern). The public API is shared via `./types`, so both forks expose
19
+ * an identical, generic, strongly-typed surface.
20
+ */
21
+ import * as React from 'react';
22
+ import type { VirtualListHandle, VirtualListProps } from './types';
23
+ export type { VirtualListHandle, VirtualListProps, VirtualListRenderItem, VirtualListRenderItemInfo, VirtualListSlot, } from './types';
24
+ declare const VirtualList: <T>(props: VirtualListProps<T> & {
25
+ ref?: React.Ref<VirtualListHandle>;
26
+ }) => React.ReactElement;
27
+ export { VirtualList };
28
+ export default VirtualList;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/list/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,yBAAyB,EACzB,eAAe,GAChB,MAAM,SAAS,CAAC;AAgFjB,QAAA,MAAM,WAAW,EAA+C,CAAC,CAAC,EAChE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,YAAY,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `VirtualList` — WEB variant. A document-scroll (window) virtualizer built on
3
+ * `@tanstack/react-virtual`'s `useWindowVirtualizer`, mirroring the proven
4
+ * Mention feed virtualizer. The BODY scrolls (so scrolling works from anywhere,
5
+ * including over sticky side columns) and only the rows in the virtual window
6
+ * are mounted, so the DOM stays bounded.
7
+ *
8
+ * Why this exists (the bug it fixes): a previous app-local list used the same
9
+ * hook but rendered ZERO rows in production — the wrapper grew to the right
10
+ * height (`getTotalSize()` was correct) yet `getVirtualItems()` came back empty,
11
+ * so only the header painted. Root cause: `useWindowVirtualizer` can latch an
12
+ * initial frame in which the window rect measured as `0` (or `scrollMargin` was
13
+ * still stale), which leaves the computed range empty; a STATIC, finite list
14
+ * re-renders too few times to self-correct and sticks on that bad frame. (The
15
+ * feed only survived by incidental re-render churn — data/loading updates and
16
+ * scroll/intersection observers — which a plain list does not have.)
17
+ *
18
+ * This implementation is SELF-SUFFICIENT and does not rely on incidental churn:
19
+ *
20
+ * 1. `scrollMargin` (the wrapper's offset from the document top) is measured in
21
+ * a layout effect AND re-synced on window `resize`.
22
+ * 2. An explicit post-mount recompute (`virtualizer.measure()`) forces the
23
+ * range to be recomputed once the real viewport height + offset are known —
24
+ * guaranteeing a short list mounts EVERY row in the first viewport even with
25
+ * zero further re-renders. Re-run on resize.
26
+ * 3. The measured spacer is `Math.max(totalSize, lastItemEnd)`, so it always
27
+ * contains every absolutely-positioned row even when `scrollMargin` is
28
+ * momentarily stale; the document therefore always grows to the full content
29
+ * height (keeping sticky side rails pinned) and the window-scroll geometry
30
+ * the virtualizer reads stays consistent.
31
+ * 4. Per-row `measureElement` refs are STABLE (cached per key), so rows are not
32
+ * detached/re-measured on every render.
33
+ *
34
+ * Native bundlers use `./index.tsx` (a `FlatList` wrapper); web bundlers select
35
+ * this file via the `"browser"` export condition in `package.json`.
36
+ */
37
+ import * as React from 'react';
38
+ import type { VirtualListHandle, VirtualListProps } from './types';
39
+ export type { VirtualListHandle, VirtualListProps, VirtualListRenderItem, VirtualListRenderItemInfo, VirtualListSlot, } from './types';
40
+ declare const VirtualList: <T>(props: VirtualListProps<T> & {
41
+ ref?: React.Ref<VirtualListHandle>;
42
+ }) => React.ReactElement;
43
+ export { VirtualList };
44
+ export default VirtualList;
45
+ //# sourceMappingURL=index.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/list/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,yBAAyB,EACzB,eAAe,GAChB,MAAM,SAAS,CAAC;AA0MjB,QAAA,MAAM,WAAW,EAA4C,CAAC,CAAC,EAC7D,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,YAAY,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
@@ -0,0 +1,77 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ /** Info passed to {@link VirtualListProps.renderItem} for each row. */
4
+ export interface VirtualListRenderItemInfo<T> {
5
+ item: T;
6
+ index: number;
7
+ }
8
+ /** Row renderer. Returns the element for a single item (or `null` to skip). */
9
+ export type VirtualListRenderItem<T> = (info: VirtualListRenderItemInfo<T>) => ReactElement | null;
10
+ /**
11
+ * A header / empty / footer slot. Either a ready element or a thunk that
12
+ * produces one — both `<X />` and `() => <X />` call sites are supported,
13
+ * matching React Native `FlatList` slot ergonomics.
14
+ */
15
+ export type VirtualListSlot = ReactElement | (() => ReactElement | null) | null | undefined;
16
+ /**
17
+ * Props for {@link VirtualList} — the canonical cross-platform virtualized list
18
+ * for the Oxy ecosystem. The shape is a precise drop-in for the `FlatList` /
19
+ * `@legendapp/list` call sites it replaces: every accepted prop is listed
20
+ * explicitly (no index signature, no `any`).
21
+ */
22
+ export interface VirtualListProps<T> {
23
+ /** Row data. */
24
+ data?: readonly T[] | null;
25
+ /** Renders one row. */
26
+ renderItem?: VirtualListRenderItem<T>;
27
+ /** Stable key for a row; strongly recommended for correct virtualization. */
28
+ keyExtractor?: (item: T, index: number) => string;
29
+ /** Rendered above the rows; scrolls away with the content. */
30
+ ListHeaderComponent?: VirtualListSlot;
31
+ /** Rendered (in place of rows) when `data` is empty. */
32
+ ListEmptyComponent?: VirtualListSlot;
33
+ /** Rendered below the rows. */
34
+ ListFooterComponent?: VirtualListSlot;
35
+ /** Style for the outer list container. */
36
+ style?: StyleProp<ViewStyle>;
37
+ /** Style for the inner content wrapper (e.g. padding). */
38
+ contentContainerStyle?: StyleProp<ViewStyle>;
39
+ /**
40
+ * First-paint row-height estimate (px) used before a row is measured. On web
41
+ * each row is measured after mount and this is only the initial guess.
42
+ */
43
+ estimatedItemSize?: number;
44
+ /** Web: extra rows mounted beyond the viewport (virtualization overscan). */
45
+ overscan?: number;
46
+ /** Called when the end of the list is approached (native pagination). */
47
+ onEndReached?: () => void;
48
+ /** Native: viewport-relative distance from the end that fires `onEndReached`. */
49
+ onEndReachedThreshold?: number;
50
+ /** Pull-to-refresh active flag (native). */
51
+ refreshing?: boolean;
52
+ /** Pull-to-refresh handler (native). */
53
+ onRefresh?: () => void;
54
+ /** Test id applied to the outer container. */
55
+ testID?: string;
56
+ removeClippedSubviews?: boolean;
57
+ maxToRenderPerBatch?: number;
58
+ windowSize?: number;
59
+ initialNumToRender?: number;
60
+ recycleItems?: boolean;
61
+ maintainVisibleContentPosition?: boolean;
62
+ }
63
+ /** Imperative handle exposed via `ref`. */
64
+ export interface VirtualListHandle {
65
+ /** Scroll to an absolute offset (px). Web scrolls the document. */
66
+ scrollToOffset: (params?: {
67
+ offset?: number;
68
+ animated?: boolean;
69
+ }) => void;
70
+ /** Scroll to a coordinate. Web scrolls the document to `y`. */
71
+ scrollTo: (params?: {
72
+ x?: number;
73
+ y?: number;
74
+ animated?: boolean;
75
+ }) => void;
76
+ }
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;CACf;AAED,+EAA+E;AAC/E,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,CACrC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAC/B,YAAY,GAAG,IAAI,CAAC;AAEzB;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,GAC3B,IAAI,GACJ,SAAS,CAAC;AAEd;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,gBAAgB;IAChB,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;IAC3B,uBAAuB;IACvB,UAAU,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACtC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,eAAe,CAAC;IACrC,+BAA+B;IAC/B,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAMhB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,cAAc,EAAE,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,+DAA+D;IAC/D,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"LinkPreviewCard.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/LinkPreviewCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAqC,MAAM,OAAO,CAAC;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA0HpD,eAAO,MAAM,eAAe,kDAAiC,CAAC"}
1
+ {"version":3,"file":"LinkPreviewCard.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/LinkPreviewCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAqC,MAAM,OAAO,CAAC;AAY1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA8HpD,eAAO,MAAM,eAAe,kDAAiC,CAAC"}
@@ -22,6 +22,18 @@ export interface LinkPreviewCardProps {
22
22
  image?: string;
23
23
  /** Site / brand name shown above the title. Falls back to the URL hostname. */
24
24
  siteName?: string;
25
+ /**
26
+ * Fill-height mode for the cover image. Defaults to `false`.
27
+ *
28
+ * - `false` (default): the cover image keeps its fixed intrinsic height —
29
+ * correct when the card sizes itself (e.g. a link rendered alone).
30
+ * - `true`: the cover image flexes (`flex: 1`, no fixed height) to fill
31
+ * whatever vertical space the card is given, with the text block as a
32
+ * compact intrinsic-height footer below it. The consumer supplies the
33
+ * bounding height via `style` (e.g. `style={{ height: 180 }}`) so the card
34
+ * fits a fixed-height row alongside other attachments without overflowing.
35
+ */
36
+ coverFill?: boolean;
25
37
  /** Press handler. When omitted, the card opens `url` via `Linking.openURL`. */
26
38
  onPress?: () => void;
27
39
  /** Extra NativeWind utilities merged onto the card surface. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/link-preview/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `VirtualList` — the canonical cross-platform virtualized list for the Oxy
3
+ * ecosystem. Every app uses this ONE implementation for grouped/stacked row
4
+ * lists (who-to-follow, starter packs, connections, settings rows, …) so the
5
+ * virtualization behaviour is identical everywhere.
6
+ *
7
+ * NATIVE variant: a thin, correctly-typed wrapper over React Native `FlatList`.
8
+ * The consumer lists this component replaces are short and uniform, so the
9
+ * platform's own virtualization is the simplest robust choice — no
10
+ * `@legendapp/list`, no app-specific scroll bridge, nothing for a consumer to
11
+ * wire up. Bloom stays app-agnostic.
12
+ *
13
+ * WEB variant (`index.web.tsx`, selected via the `"browser"` export condition):
14
+ * a document-scroll window virtualizer built on `@tanstack/react-virtual`.
15
+ *
16
+ * This default file has NO platform-only imports, so consumer `tsc` and web
17
+ * bundlers resolve it cleanly (mirrors the `../scroll` / `../content-panel`
18
+ * fork pattern). The public API is shared via `./types`, so both forks expose
19
+ * an identical, generic, strongly-typed surface.
20
+ */
21
+ import * as React from 'react';
22
+ import type { VirtualListHandle, VirtualListProps } from './types';
23
+ export type { VirtualListHandle, VirtualListProps, VirtualListRenderItem, VirtualListRenderItemInfo, VirtualListSlot, } from './types';
24
+ declare const VirtualList: <T>(props: VirtualListProps<T> & {
25
+ ref?: React.Ref<VirtualListHandle>;
26
+ }) => React.ReactElement;
27
+ export { VirtualList };
28
+ export default VirtualList;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/list/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,yBAAyB,EACzB,eAAe,GAChB,MAAM,SAAS,CAAC;AAgFjB,QAAA,MAAM,WAAW,EAA+C,CAAC,CAAC,EAChE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,YAAY,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `VirtualList` — WEB variant. A document-scroll (window) virtualizer built on
3
+ * `@tanstack/react-virtual`'s `useWindowVirtualizer`, mirroring the proven
4
+ * Mention feed virtualizer. The BODY scrolls (so scrolling works from anywhere,
5
+ * including over sticky side columns) and only the rows in the virtual window
6
+ * are mounted, so the DOM stays bounded.
7
+ *
8
+ * Why this exists (the bug it fixes): a previous app-local list used the same
9
+ * hook but rendered ZERO rows in production — the wrapper grew to the right
10
+ * height (`getTotalSize()` was correct) yet `getVirtualItems()` came back empty,
11
+ * so only the header painted. Root cause: `useWindowVirtualizer` can latch an
12
+ * initial frame in which the window rect measured as `0` (or `scrollMargin` was
13
+ * still stale), which leaves the computed range empty; a STATIC, finite list
14
+ * re-renders too few times to self-correct and sticks on that bad frame. (The
15
+ * feed only survived by incidental re-render churn — data/loading updates and
16
+ * scroll/intersection observers — which a plain list does not have.)
17
+ *
18
+ * This implementation is SELF-SUFFICIENT and does not rely on incidental churn:
19
+ *
20
+ * 1. `scrollMargin` (the wrapper's offset from the document top) is measured in
21
+ * a layout effect AND re-synced on window `resize`.
22
+ * 2. An explicit post-mount recompute (`virtualizer.measure()`) forces the
23
+ * range to be recomputed once the real viewport height + offset are known —
24
+ * guaranteeing a short list mounts EVERY row in the first viewport even with
25
+ * zero further re-renders. Re-run on resize.
26
+ * 3. The measured spacer is `Math.max(totalSize, lastItemEnd)`, so it always
27
+ * contains every absolutely-positioned row even when `scrollMargin` is
28
+ * momentarily stale; the document therefore always grows to the full content
29
+ * height (keeping sticky side rails pinned) and the window-scroll geometry
30
+ * the virtualizer reads stays consistent.
31
+ * 4. Per-row `measureElement` refs are STABLE (cached per key), so rows are not
32
+ * detached/re-measured on every render.
33
+ *
34
+ * Native bundlers use `./index.tsx` (a `FlatList` wrapper); web bundlers select
35
+ * this file via the `"browser"` export condition in `package.json`.
36
+ */
37
+ import * as React from 'react';
38
+ import type { VirtualListHandle, VirtualListProps } from './types';
39
+ export type { VirtualListHandle, VirtualListProps, VirtualListRenderItem, VirtualListRenderItemInfo, VirtualListSlot, } from './types';
40
+ declare const VirtualList: <T>(props: VirtualListProps<T> & {
41
+ ref?: React.Ref<VirtualListHandle>;
42
+ }) => React.ReactElement;
43
+ export { VirtualList };
44
+ export default VirtualList;
45
+ //# sourceMappingURL=index.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/list/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,yBAAyB,EACzB,eAAe,GAChB,MAAM,SAAS,CAAC;AA0MjB,QAAA,MAAM,WAAW,EAA4C,CAAC,CAAC,EAC7D,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,YAAY,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
@@ -0,0 +1,77 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ /** Info passed to {@link VirtualListProps.renderItem} for each row. */
4
+ export interface VirtualListRenderItemInfo<T> {
5
+ item: T;
6
+ index: number;
7
+ }
8
+ /** Row renderer. Returns the element for a single item (or `null` to skip). */
9
+ export type VirtualListRenderItem<T> = (info: VirtualListRenderItemInfo<T>) => ReactElement | null;
10
+ /**
11
+ * A header / empty / footer slot. Either a ready element or a thunk that
12
+ * produces one — both `<X />` and `() => <X />` call sites are supported,
13
+ * matching React Native `FlatList` slot ergonomics.
14
+ */
15
+ export type VirtualListSlot = ReactElement | (() => ReactElement | null) | null | undefined;
16
+ /**
17
+ * Props for {@link VirtualList} — the canonical cross-platform virtualized list
18
+ * for the Oxy ecosystem. The shape is a precise drop-in for the `FlatList` /
19
+ * `@legendapp/list` call sites it replaces: every accepted prop is listed
20
+ * explicitly (no index signature, no `any`).
21
+ */
22
+ export interface VirtualListProps<T> {
23
+ /** Row data. */
24
+ data?: readonly T[] | null;
25
+ /** Renders one row. */
26
+ renderItem?: VirtualListRenderItem<T>;
27
+ /** Stable key for a row; strongly recommended for correct virtualization. */
28
+ keyExtractor?: (item: T, index: number) => string;
29
+ /** Rendered above the rows; scrolls away with the content. */
30
+ ListHeaderComponent?: VirtualListSlot;
31
+ /** Rendered (in place of rows) when `data` is empty. */
32
+ ListEmptyComponent?: VirtualListSlot;
33
+ /** Rendered below the rows. */
34
+ ListFooterComponent?: VirtualListSlot;
35
+ /** Style for the outer list container. */
36
+ style?: StyleProp<ViewStyle>;
37
+ /** Style for the inner content wrapper (e.g. padding). */
38
+ contentContainerStyle?: StyleProp<ViewStyle>;
39
+ /**
40
+ * First-paint row-height estimate (px) used before a row is measured. On web
41
+ * each row is measured after mount and this is only the initial guess.
42
+ */
43
+ estimatedItemSize?: number;
44
+ /** Web: extra rows mounted beyond the viewport (virtualization overscan). */
45
+ overscan?: number;
46
+ /** Called when the end of the list is approached (native pagination). */
47
+ onEndReached?: () => void;
48
+ /** Native: viewport-relative distance from the end that fires `onEndReached`. */
49
+ onEndReachedThreshold?: number;
50
+ /** Pull-to-refresh active flag (native). */
51
+ refreshing?: boolean;
52
+ /** Pull-to-refresh handler (native). */
53
+ onRefresh?: () => void;
54
+ /** Test id applied to the outer container. */
55
+ testID?: string;
56
+ removeClippedSubviews?: boolean;
57
+ maxToRenderPerBatch?: number;
58
+ windowSize?: number;
59
+ initialNumToRender?: number;
60
+ recycleItems?: boolean;
61
+ maintainVisibleContentPosition?: boolean;
62
+ }
63
+ /** Imperative handle exposed via `ref`. */
64
+ export interface VirtualListHandle {
65
+ /** Scroll to an absolute offset (px). Web scrolls the document. */
66
+ scrollToOffset: (params?: {
67
+ offset?: number;
68
+ animated?: boolean;
69
+ }) => void;
70
+ /** Scroll to a coordinate. Web scrolls the document to `y`. */
71
+ scrollTo: (params?: {
72
+ x?: number;
73
+ y?: number;
74
+ animated?: boolean;
75
+ }) => void;
76
+ }
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/list/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;CACf;AAED,+EAA+E;AAC/E,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,CACrC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAC/B,YAAY,GAAG,IAAI,CAAC;AAEzB;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,GAC3B,IAAI,GACJ,SAAS,CAAC;AAEd;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,gBAAgB;IAChB,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;IAC3B,uBAAuB;IACvB,UAAU,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACtC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,eAAe,CAAC;IACrC,+BAA+B;IAC/B,mBAAmB,CAAC,EAAE,eAAe,CAAC;IACtC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7C;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAMhB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,cAAc,EAAE,CAAC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E,+DAA+D;IAC/D,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7E"}