react-anchorlist 0.3.3 → 0.3.4
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.
- package/README.md +151 -169
- package/dist/components/ChatVirtualList.d.ts.map +1 -1
- package/dist/components/VirtualList.d.ts.map +1 -1
- package/dist/hooks/useScrollAnchor.d.ts +5 -0
- package/dist/hooks/useScrollAnchor.d.ts.map +1 -1
- package/dist/hooks/useVirtualEngine.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +384 -353
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -1
package/README.md
CHANGED
|
@@ -1,202 +1,129 @@
|
|
|
1
1
|
# react-anchorlist
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
High-performance virtualized lists for React, optimized for chat and infinite feeds.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
No flicker when prepending older messages. Stable scroll. Simple API.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install react-anchorlist
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Why use it
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- Virtualizes large lists (renders only what is visible + overscan)
|
|
14
|
+
- Keeps scroll stable when you prepend items (chat history)
|
|
15
|
+
- Supports top/bottom pagination callbacks
|
|
16
|
+
- Includes chat-focused behavior like `followOutput` and declarative scroll commands
|
|
17
|
+
- Works with dynamic row heights via `ResizeObserver`
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
Ela mantém a navegação suave mesmo com histórico extenso, reduzindo custo de renderização e melhorando a experiência do usuário.
|
|
19
|
+
## 60-second setup
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Para que serve
|
|
21
|
-
|
|
22
|
-
Use quando você precisa de:
|
|
23
|
-
|
|
24
|
-
- **Renderização eficiente** em listas longas
|
|
25
|
-
- **Scroll estável** ao adicionar itens no topo (padrão chat)
|
|
26
|
-
- **Paginação incremental** ao chegar no início/fim da lista
|
|
27
|
-
- **Comportamento previsível** para “seguir no final” quando chegam novos itens
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Como funciona (alto nível)
|
|
32
|
-
|
|
33
|
-
- Renderiza principalmente os itens visíveis (com pequeno overscan)
|
|
34
|
-
- Mede altura real dos itens para manter posicionamento correto
|
|
35
|
-
- Preserva âncora de scroll quando itens são inseridos no topo
|
|
36
|
-
- Dispara callbacks ao aproximar do topo/fim para buscar mais dados
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Quick start
|
|
41
|
-
|
|
42
|
-
### Lista genérica (tickets, contatos, feed)
|
|
21
|
+
### Generic list
|
|
43
22
|
|
|
44
23
|
```tsx
|
|
45
|
-
import { VirtualList } from
|
|
24
|
+
import { VirtualList } from "react-anchorlist"
|
|
46
25
|
|
|
47
26
|
<VirtualList
|
|
48
27
|
data={tickets}
|
|
49
|
-
computeItemKey={(
|
|
50
|
-
itemContent={(
|
|
28
|
+
computeItemKey={(_, item) => item.id}
|
|
29
|
+
itemContent={(_, item) => <TicketRow ticket={item} />}
|
|
51
30
|
onEndReached={loadMore}
|
|
52
|
-
style={{ height:
|
|
31
|
+
style={{ height: "100%" }}
|
|
53
32
|
/>
|
|
54
33
|
```
|
|
55
34
|
|
|
56
|
-
###
|
|
35
|
+
### Chat list (recommended pattern)
|
|
57
36
|
|
|
58
37
|
```tsx
|
|
59
|
-
import {
|
|
60
|
-
import
|
|
38
|
+
import { useState } from "react"
|
|
39
|
+
import { ChatVirtualList } from "react-anchorlist"
|
|
40
|
+
import type { ChatScrollModifier } from "react-anchorlist"
|
|
61
41
|
|
|
62
|
-
const
|
|
42
|
+
const [scrollModifier, setScrollModifier] = useState<ChatScrollModifier | null>(null)
|
|
63
43
|
|
|
64
44
|
<ChatVirtualList
|
|
65
|
-
ref={listRef}
|
|
66
45
|
data={messages}
|
|
67
|
-
computeItemKey={(
|
|
68
|
-
itemContent={(
|
|
69
|
-
// Paginação — dispara ao chegar no topo
|
|
70
|
-
onStartReached={() => {
|
|
71
|
-
setScrollModifier({ id: `prepend-${Date.now()}`, type: 'prepend' })
|
|
72
|
-
loadOlderMessages()
|
|
73
|
-
}}
|
|
74
|
-
// API declarativa para operações de scroll
|
|
46
|
+
computeItemKey={(_, item) => item._id}
|
|
47
|
+
itemContent={(_, item) => <Message data={item} />}
|
|
75
48
|
scrollModifier={scrollModifier}
|
|
76
|
-
// Mantém no final quando chegam novas mensagens
|
|
77
49
|
followOutput="auto"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
onStartReached={async () => {
|
|
51
|
+
// 1) tell the list to preserve anchor
|
|
52
|
+
setScrollModifier({ id: `prepend-${Date.now()}`, type: "prepend" })
|
|
53
|
+
// 2) prepend older messages in your state
|
|
54
|
+
await loadOlderMessages()
|
|
83
55
|
}}
|
|
84
|
-
|
|
56
|
+
onAtBottomChange={setIsAtBottom}
|
|
57
|
+
style={{ height: "100%" }}
|
|
85
58
|
/>
|
|
86
59
|
```
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
## ChatVirtualList props
|
|
91
|
-
|
|
92
|
-
| Prop | Type | Default | Description |
|
|
93
|
-
|---|---|---|---|
|
|
94
|
-
| `data` | `T[]` | required | Array de itens |
|
|
95
|
-
| `itemContent` | `(index, item) => ReactNode` | required | Função de renderização |
|
|
96
|
-
| `computeItemKey` | `(index, item) => string \| number` | required | Chave estável por item |
|
|
97
|
-
| `estimatedItemSize` | `number` | `80` | Estimativa inicial de altura (px) |
|
|
98
|
-
| `overscan` | `number` | `20` | Itens renderizados além da área visível |
|
|
99
|
-
| `followOutput` | `"auto" \| "smooth" \| false` | `"auto"` | Seguir no final ao entrar item novo |
|
|
100
|
-
| `atBottomThreshold` | `number` | `200` | Distância (px) para considerar “no final” |
|
|
101
|
-
| `atBottomHysteresis` | `{ enter: number; leave: number }` | `{ enter: 80, leave: 160 }` | Evita alternância excessiva do estado "at bottom" |
|
|
102
|
-
| `initialAlignment` | `"top" \| "bottom"` | `"bottom"` | Posição inicial do scroll |
|
|
103
|
-
| `scrollModifier` | `ChatScrollModifier` | `null` | Comando declarativo para prepend/append/jump |
|
|
104
|
-
| `onStartReached` | `() => void \| Promise<void>` | — | Dispara ao aproximar do topo |
|
|
105
|
-
| `onEndReached` | `() => void \| Promise<void>` | — | Dispara ao aproximar do fim |
|
|
106
|
-
| `startReachedThreshold` | `number` | `300` | Distância (px) do topo para disparar callback |
|
|
107
|
-
| `endReachedThreshold` | `number` | `300` | Distância (px) do fim para disparar callback |
|
|
108
|
-
| `onAtBottomChange` | `(isAtBottom: boolean) => void` | — | Mudança de estado “no final” |
|
|
109
|
-
| `scrollToMessageKey` | `string \| number \| null` | — | **Deprecated**. Use `scrollModifier` com `type: "jump-to-key"` |
|
|
110
|
-
| `onScrollToMessageComplete` | `() => void` | — | **Deprecated** |
|
|
111
|
-
| `components` | `{ Header, Footer, EmptyPlaceholder }` | — | Slots opcionais |
|
|
112
|
-
| `className` | `string` | — | Classe CSS do container |
|
|
113
|
-
| `style` | `CSSProperties` | — | Estilo inline do container |
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## VirtualList props
|
|
118
|
-
|
|
119
|
-
| Prop | Type | Default | Description |
|
|
120
|
-
|---|---|---|---|
|
|
121
|
-
| `data` | `T[]` | required | Array de itens |
|
|
122
|
-
| `itemContent` | `(index, item) => ReactNode` | required | Função de renderização |
|
|
123
|
-
| `computeItemKey` | `(index, item) => string \| number` | required | Chave estável por item |
|
|
124
|
-
| `estimatedItemSize` | `number` | `60` | Estimativa inicial de altura (px) |
|
|
125
|
-
| `overscan` | `number` | `20` | Itens renderizados além da área visível |
|
|
126
|
-
| `onEndReached` | `() => void \| Promise<void>` | — | Dispara ao aproximar do fim |
|
|
127
|
-
| `endReachedThreshold` | `number` | `300` | Distância (px) do fim para disparar callback |
|
|
128
|
-
| `components` | `{ Header, Footer, EmptyPlaceholder }` | — | Slots opcionais |
|
|
129
|
-
| `className` | `string` | — | Classe CSS do container |
|
|
130
|
-
| `style` | `CSSProperties` | — | Estilo inline do container |
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## Ref handle (ChatVirtualList)
|
|
61
|
+
## Core concept (important)
|
|
135
62
|
|
|
136
|
-
|
|
137
|
-
const listRef = useRef<ChatVirtualListHandle>(null)
|
|
63
|
+
When data changes, control scroll behavior with `scrollModifier`:
|
|
138
64
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
65
|
+
```ts
|
|
66
|
+
type ChatScrollModifier =
|
|
67
|
+
| { id: string | number; type: "prepend" }
|
|
68
|
+
| { id: string | number; type: "append"; behavior?: "auto" | "smooth"; ifAtBottomOnly?: boolean }
|
|
69
|
+
| { id: string | number; type: "items-change" }
|
|
70
|
+
| { id: string | number; type: "jump-to-key"; key: string | number; align?: "start" | "center" | "end"; behavior?: ScrollBehavior }
|
|
145
71
|
```
|
|
146
72
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
| `scrollToKey(key, opts?)` | Vai para item por chave |
|
|
152
|
-
| `getScrollTop()` | Posição atual do scroll |
|
|
153
|
-
| `isAtBottom()` | Indica se está no final |
|
|
154
|
-
| `prepareAnchor()` | **Deprecated**. Use `scrollModifier={{ id, type: 'prepend' }}` |
|
|
73
|
+
- `id` must be unique for each command.
|
|
74
|
+
- `prepend` keeps viewport position stable while older messages are added on top.
|
|
75
|
+
- `append` can auto-scroll to bottom.
|
|
76
|
+
- `jump-to-key` scrolls to one specific item.
|
|
155
77
|
|
|
156
|
-
|
|
78
|
+
## API quick reference
|
|
157
79
|
|
|
158
|
-
|
|
80
|
+
### Exports
|
|
159
81
|
|
|
160
82
|
```ts
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
83
|
+
import {
|
|
84
|
+
ChatVirtualList,
|
|
85
|
+
VirtualList,
|
|
86
|
+
useChatVirtualizer,
|
|
87
|
+
usePagination,
|
|
88
|
+
} from "react-anchorlist"
|
|
166
89
|
```
|
|
167
90
|
|
|
168
|
-
|
|
169
|
-
- `prepend` prepara e restaura âncora automaticamente no próximo update de dados.
|
|
170
|
-
- `jump-to-key` substitui `scrollToMessageKey`.
|
|
91
|
+
### `ChatVirtualList` most-used props
|
|
171
92
|
|
|
172
|
-
|
|
93
|
+
- `data`, `itemContent`, `computeItemKey` (required)
|
|
94
|
+
- `scrollModifier` (`ChatScrollModifier | null`)
|
|
95
|
+
- `followOutput` (`"auto" | "smooth" | false`, default: `"auto"`)
|
|
96
|
+
- `onStartReached`, `onEndReached`
|
|
97
|
+
- `startReachedThreshold` and `endReachedThreshold` (default: `300`)
|
|
98
|
+
- `onAtBottomChange`
|
|
99
|
+
- `estimatedItemSize` (default: `80`)
|
|
100
|
+
- `overscan` (default: `20`)
|
|
173
101
|
|
|
174
|
-
|
|
102
|
+
### `VirtualList` most-used props
|
|
175
103
|
|
|
176
|
-
|
|
104
|
+
- `data`, `itemContent`, `computeItemKey` (required)
|
|
105
|
+
- `onEndReached`
|
|
106
|
+
- `endReachedThreshold` (default: `300`)
|
|
107
|
+
- `estimatedItemSize` (default: `60`)
|
|
108
|
+
- `overscan` (default: `20`)
|
|
177
109
|
|
|
178
|
-
|
|
179
|
-
listRef.current?.prepareAnchor()
|
|
180
|
-
await loadMoreMessages()
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Depois:
|
|
110
|
+
### `ChatVirtualList` ref handle
|
|
184
111
|
|
|
185
112
|
```tsx
|
|
186
|
-
|
|
187
|
-
|
|
113
|
+
listRef.current?.scrollToBottom()
|
|
114
|
+
listRef.current?.scrollToIndex(42, { align: "center", behavior: "smooth" })
|
|
115
|
+
listRef.current?.scrollToKey("msg-123", { align: "center" })
|
|
116
|
+
listRef.current?.getScrollTop()
|
|
117
|
+
listRef.current?.isAtBottom()
|
|
188
118
|
```
|
|
189
119
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
## usePagination hook
|
|
193
|
-
|
|
194
|
-
Para paginação orientada ao back-end com deduplicação automática:
|
|
120
|
+
## `usePagination` hook (optional helper)
|
|
195
121
|
|
|
196
122
|
```tsx
|
|
197
|
-
import {
|
|
123
|
+
import { useEffect } from "react"
|
|
124
|
+
import { usePagination, ChatVirtualList } from "react-anchorlist"
|
|
198
125
|
|
|
199
|
-
const { items,
|
|
126
|
+
const { items, hasPrevPage, loadPrevPage, loadingMore, refresh } = usePagination({
|
|
200
127
|
fetcher: async (page) => {
|
|
201
128
|
const res = await api.get(`/messages?page=${page}&per_page=50`)
|
|
202
129
|
return {
|
|
@@ -206,41 +133,96 @@ const { items, loadPrevPage, hasPrevPage, loadingMore } = usePagination({
|
|
|
206
133
|
currentPage: res.pagination.current_page,
|
|
207
134
|
}
|
|
208
135
|
},
|
|
209
|
-
direction:
|
|
210
|
-
getKey: (msg) => msg._id,
|
|
136
|
+
direction: "prepend",
|
|
137
|
+
getKey: (msg) => msg._id,
|
|
211
138
|
})
|
|
212
139
|
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
refresh() // load initial page
|
|
142
|
+
}, [refresh])
|
|
143
|
+
|
|
213
144
|
<ChatVirtualList
|
|
214
145
|
data={items}
|
|
215
146
|
computeItemKey={(_, item) => item._id}
|
|
216
147
|
itemContent={(_, item) => <Message data={item} />}
|
|
217
148
|
onStartReached={hasPrevPage ? loadPrevPage : undefined}
|
|
218
149
|
components={{
|
|
219
|
-
Header: () => loadingMore ? <Spinner /> : null,
|
|
150
|
+
Header: () => (loadingMore ? <Spinner /> : null),
|
|
220
151
|
}}
|
|
221
152
|
/>
|
|
222
153
|
```
|
|
223
154
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
155
|
+
## Best practices
|
|
156
|
+
|
|
157
|
+
- Always use stable keys in `computeItemKey`.
|
|
158
|
+
- Keep `itemContent` lightweight.
|
|
159
|
+
- Start with a realistic `estimatedItemSize`.
|
|
160
|
+
- Keep `overscan` low unless you need smoother very fast scrolling.
|
|
161
|
+
- Prefer `scrollModifier` over deprecated APIs (`prepareAnchor`, `scrollToMessageKey`).
|
|
162
|
+
|
|
163
|
+
## Internals (simple)
|
|
164
|
+
|
|
165
|
+
- `OffsetMap`: stores cumulative offsets per item
|
|
166
|
+
- Anchor snapshot: keeps visual position stable on prepend
|
|
167
|
+
- Per-item `ResizeObserver`: updates real row heights
|
|
168
|
+
- Binary search: quickly finds visible range
|
|
169
|
+
|
|
170
|
+
## Keywords and discoverability
|
|
171
|
+
|
|
172
|
+
If your goal is npm discovery, keywords belong in `package.json` (not only in README text).
|
|
173
|
+
|
|
174
|
+
Suggested scope for this lib:
|
|
175
|
+
- `react`
|
|
176
|
+
- `virtual-list`
|
|
177
|
+
- `virtualization`
|
|
178
|
+
- `virtual-scroll`
|
|
179
|
+
- `chat`
|
|
180
|
+
- `infinite-scroll`
|
|
181
|
+
- `scroll-anchor`
|
|
182
|
+
|
|
183
|
+
## Copy-paste AI prompt
|
|
184
|
+
|
|
185
|
+
Use this prompt in ChatGPT/Claude/Cursor/GitHub Copilot Chat:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
You are a senior React engineer. Integrate the npm library `react-anchorlist` into my app.
|
|
189
|
+
|
|
190
|
+
Context:
|
|
191
|
+
- Stack: [React version + framework]
|
|
192
|
+
- Data type: [message/ticket/feed item shape]
|
|
193
|
+
- Item unique key: [id field]
|
|
194
|
+
- List container height strategy: [fixed/flex/full-screen]
|
|
195
|
+
|
|
196
|
+
Goal:
|
|
197
|
+
Implement a production-ready virtualized list with smooth scrolling and correct pagination behavior.
|
|
198
|
+
|
|
199
|
+
Requirements:
|
|
200
|
+
1) Use `ChatVirtualList` for chat-like UX (prepend older items at top).
|
|
201
|
+
2) Use stable `computeItemKey`.
|
|
202
|
+
3) Use `scrollModifier` commands correctly:
|
|
203
|
+
- before loading older items: `{ id: uniqueId, type: "prepend" }`
|
|
204
|
+
- when appending new realtime items: use append/items-change behavior when appropriate
|
|
205
|
+
- use `jump-to-key` for "scroll to message"
|
|
206
|
+
4) Keep `followOutput="auto"` and expose `onAtBottomChange`.
|
|
207
|
+
5) Wire `onStartReached` and/or `onEndReached` to my pagination functions.
|
|
208
|
+
6) Add proper TypeScript types.
|
|
209
|
+
7) Include minimal CSS/container setup so scrolling works (`height` + `overflow`).
|
|
210
|
+
8) Avoid deprecated APIs (`prepareAnchor`, `scrollToMessageKey`) unless migration support is explicitly requested.
|
|
211
|
+
|
|
212
|
+
Deliverables:
|
|
213
|
+
- Full component code ready to paste.
|
|
214
|
+
- State management for `scrollModifier`.
|
|
215
|
+
- Example handlers: `loadOlderMessages`, `loadNewerMessages`.
|
|
216
|
+
- Brief explanation of why the scroll stays stable on prepend.
|
|
217
|
+
- Optional: a second example using `VirtualList` for non-chat pages.
|
|
218
|
+
|
|
219
|
+
Project data to use:
|
|
220
|
+
- Messages state variable: [name here]
|
|
221
|
+
- Pagination function names: [names here]
|
|
222
|
+
- Message row component name: [name here]
|
|
223
|
+
|
|
224
|
+
Return clean, runnable code with no placeholders left.
|
|
225
|
+
```
|
|
244
226
|
|
|
245
227
|
## License
|
|
246
228
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatVirtualList.d.ts","sourceRoot":"","sources":["../../src/components/ChatVirtualList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAe,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ChatVirtualList.d.ts","sourceRoot":"","sources":["../../src/components/ChatVirtualList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAe,MAAM,UAAU,CAAA;AAyHxF,eAAO,MAAM,eAAe,EAAuC,CAAC,CAAC,EACnE,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;CAAE,KACxE,KAAK,CAAC,YAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualList.d.ts","sourceRoot":"","sources":["../../src/components/VirtualList.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE7D,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAC7B,IAAI,EACJ,WAAW,EACX,cAAc,EACd,iBAAsB,EACtB,QAAa,EACb,YAAY,EACZ,mBAAyB,EACzB,UAAe,EACf,SAAS,EACT,KAAK,GACN,EAAE,gBAAgB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"VirtualList.d.ts","sourceRoot":"","sources":["../../src/components/VirtualList.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE7D,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAC7B,IAAI,EACJ,WAAW,EACX,cAAc,EACd,iBAAsB,EACtB,QAAa,EACb,YAAY,EACZ,mBAAyB,EACzB,UAAe,EACf,SAAS,EACT,KAAK,GACN,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CA2DrB"}
|
|
@@ -7,6 +7,11 @@ interface UseScrollAnchorOptions {
|
|
|
7
7
|
resolveAnchorTop: (key: string | number, offsetWithinItem: number) => number | null;
|
|
8
8
|
onRestored?: () => void;
|
|
9
9
|
}
|
|
10
|
+
export declare function resolveAnchorTargetFromSnapshot(params: {
|
|
11
|
+
snapshot: AnchorSnapshot;
|
|
12
|
+
currentScrollHeight: number;
|
|
13
|
+
resolveAnchorTop: (key: string | number, offsetWithinItem: number) => number | null;
|
|
14
|
+
}): number;
|
|
10
15
|
/**
|
|
11
16
|
* Keeps viewport anchored when items are prepended.
|
|
12
17
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollAnchor.d.ts","sourceRoot":"","sources":["../../src/hooks/useScrollAnchor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,UAAU,sBAAsB;IAC9B,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,EAAE,MAAM,cAAc,GAAG,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,gBAAgB,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IACnF,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAAE,aAAa,EAAE,MAAM,IAAI,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"useScrollAnchor.d.ts","sourceRoot":"","sources":["../../src/hooks/useScrollAnchor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,UAAU,sBAAsB;IAC9B,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,qBAAqB,EAAE,MAAM,cAAc,GAAG,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,gBAAgB,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IACnF,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE;IACtD,QAAQ,EAAE,cAAc,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,gBAAgB,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;CACpF,GAAG,MAAM,CAgBT;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAAE,aAAa,EAAE,MAAM,IAAI,CAAA;CAAE,CAsF9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVirtualEngine.d.ts","sourceRoot":"","sources":["../../src/hooks/useVirtualEngine.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,sBAAsB,EAAe,MAAM,UAAU,CAAA;AAEnF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE;IAC3C,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAA;IACnD,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,KAAK,GAAG,QAAQ,CAAA;CACnC,GAAG,sBAAsB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"useVirtualEngine.d.ts","sourceRoot":"","sources":["../../src/hooks/useVirtualEngine.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,sBAAsB,EAAe,MAAM,UAAU,CAAA;AAEnF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE;IAC3C,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAA;IACnD,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,KAAK,GAAG,QAAQ,CAAA;CACnC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAgX5B"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("react/jsx-runtime"),r=require("react");function ue(l){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(l){for(const t in l)if(t!=="default"){const s=Object.getOwnPropertyDescriptor(l,t);Object.defineProperty(e,t,s.get?s:{enumerable:!0,get:()=>l[t]})}}return e.default=l,Object.freeze(e)}const G=ue(r);class ae{constructor(e,t){this.defaultSize=t,this.sizes=e>0?Array(e).fill(t):[],this.offsets=e>0?Array(e).fill(0):[],e>0&&this._recalcFrom(0)}_recalcFrom(e){for(let t=e;t<this.sizes.length;t++)this.offsets[t]=t===0?0:(this.offsets[t-1]??0)+(this.sizes[t-1]??this.defaultSize)}getOffset(e){return this.offsets[e]??0}getSize(e){return this.sizes[e]??this.defaultSize}setSize(e,t){return this.sizes[e]===t?!1:(this.sizes[e]=t,this._recalcFrom(e+1),!0)}prepend(e){const t=Array(e).fill(this.defaultSize);this.sizes=[...t,...this.sizes],this.offsets=Array(this.sizes.length).fill(0),this._recalcFrom(0)}append(e){const t=this.sizes.length;for(let s=0;s<e;s++)this.sizes.push(this.defaultSize),this.offsets.push(0);this._recalcFrom(t)}resize(e){const t=this.sizes.length;e>t?this.append(e-t):e<t&&(this.sizes=this.sizes.slice(0,e),this.offsets=this.offsets.slice(0,e))}totalSize(){if(this.sizes.length===0)return 0;const e=this.sizes.length-1;return(this.offsets[e]??0)+(this.sizes[e]??this.defaultSize)}get count(){return this.sizes.length}getOffsets(){return this.offsets}getSizes(){return this.sizes}}class fe{constructor(){this.cache=new Map}get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}has(e){return this.cache.has(e)}delete(e){this.cache.delete(e)}clear(){this.cache.clear()}applyToOffsetMap(e,t){for(const[s,i]of this.cache){const c=t.get(s);c!==void 0&&e.setSize(c,i)}}}function re(l,e){if(l.length===0)return 0;let t=0,s=l.length-1;for(;t<s;){const i=t+s>>1;(l[i]??0)<e?t=i+1:s=i}return Math.max(0,t>0&&(l[t]??0)>e?t-1:t)}function he(l,e,t){if(l.length===0)return 0;let s=0,i=l.length-1,c=0;for(;s<=i;){const n=s+i>>1;(l[n]??0)<t?(c=n,s=n+1):i=n-1}return c}function de(l){const{firstVisible:e,lastVisible:t,itemCount:s,overscan:i}=l;return s===0?{start:0,end:-1}:{start:Math.max(0,e-i),end:Math.min(s-1,t+i)}}function me(l,e,t,s){const i=s==null?void 0:s.reconcile,c=r.useRef(null);return r.useCallback((n,S)=>{const u=l.current,h=e.current;if(!u||!h)return;c.current!==null&&(cancelAnimationFrame(c.current),c.current=null);let y,f;if(typeof n=="object"&&n!==null?(y=n.index,f={align:n.align,behavior:n.behavior,offset:n.offset}):(y=n,f=S),!Number.isFinite(y))return;const m=Math.max(0,Math.min(Math.floor(y),h.count-1));h.getOffset(m),h.getSize(m);const F=(f==null?void 0:f.align)??"start",b=(f==null?void 0:f.behavior)??"auto",E=(f==null?void 0:f.offset)??0,d=()=>{var p;const H=e.current,a=l.current;if(!H||!a)return 0;const g=((p=t==null?void 0:t.current)==null?void 0:p.offsetTop)??0,z=H.getOffset(m),C=H.getSize(m);return F==="start"?g+z+E:F==="center"?g+z-a.clientHeight/2+C/2+E:g+z-a.clientHeight+C+E},w=d();((H,a)=>{typeof u.scrollTo=="function"?u.scrollTo({top:H,behavior:a}):u.scrollTop=H})(Math.max(0,w),b);const k=performance.now();let T=0;const j=12,_=300,L=1,K=()=>{c.current=null;const H=l.current;if(!H)return;const a=Math.max(0,d()),g=Math.abs(H.scrollTop-a),z=performance.now()-k>=_,C=T>=j;g<=L||z||C||(typeof H.scrollTo=="function"?H.scrollTo({top:a,behavior:"auto"}):H.scrollTop=a,T+=1,c.current=requestAnimationFrame(K))};c.current=requestAnimationFrame(K)},[l,e,t,i])}function ne(l){var ee;const{items:e,getKey:t,estimatedItemSize:s,overscan:i,initialAlignment:c}=l,n=r.useRef(null),S=r.useRef(null),u=r.useRef(null),h=r.useRef(new fe),y=r.useRef([]),f=r.useRef(new Map),m=r.useRef(e.length),F=r.useRef(null),b=r.useRef(null),E=r.useRef(!1),d=r.useRef(0),w=r.useRef(0),R=r.useRef(null),k=r.useRef(!1),T=r.useRef(null),j=r.useRef(0),_=r.useRef(0),L=r.useRef(0),K=r.useRef(null),H=r.useRef(!1),[,a]=r.useState(0),g=r.useCallback(()=>a(o=>o+1),[]);u.current||(u.current=new ae(e.length,s));const z=e.length>0?t(e[0],0):null,C=e.length>0?t(e[e.length-1],e.length-1):null;if(e.length!==m.current||z!==F.current||C!==b.current){const o=u.current,v=e.map((V,q)=>t(V,q)),x=y.current,A=x.length,M=v.length;if(M===0)o.resize(0);else if(A===0)o.resize(M);else if(M>A){const V=M-A;x.length>0&&V>=0&&v[V]===x[0]?o.prepend(V):o.resize(M)}else M<A,o.resize(M);const I=new Map;v.forEach((V,q)=>I.set(V,q)),f.current=I,h.current.applyToOffsetMap(o,I),y.current=v,m.current=e.length,F.current=z,b.current=C}const P=r.useCallback(()=>{K.current===null&&(K.current=requestAnimationFrame(()=>{K.current=null;const o=n.current;if(!o)return;const v=j.current;Math.abs(v)<.01||(j.current=0,_.current+=v,L.current+=v,o.scrollTop+=v,d.current=o.scrollTop,H.current=!0,g())}))},[g]);r.useEffect(()=>{const o=n.current;if(!o)return;const v=()=>{R.current===null&&(R.current=requestAnimationFrame(()=>{R.current=null,d.current=o.scrollTop,w.current=o.clientHeight,H.current&&(H.current=!1,_.current=0,L.current=0),g()}))};return o.addEventListener("scroll",v,{passive:!0}),()=>{o.removeEventListener("scroll",v),R.current!==null&&(cancelAnimationFrame(R.current),R.current=null),K.current!==null&&(cancelAnimationFrame(K.current),K.current=null)}},[g]),r.useEffect(()=>{const o=n.current;if(!o)return;w.current=o.clientHeight,d.current=o.scrollTop;const v=new ResizeObserver(([x])=>{x&&(w.current=x.contentRect.height,g())});return v.observe(o),()=>v.disconnect()},[g]),r.useLayoutEffect(()=>{if(E.current||e.length===0)return;const o=n.current;if(o){if(c==="bottom"){o.scrollTop=o.scrollHeight,d.current=o.scrollTop,w.current=o.clientHeight,k.current=!0,T.current!==null&&cancelAnimationFrame(T.current);const v=performance.now();let x=0,A=o.scrollHeight;const M=()=>{if(!k.current){T.current=null;return}const I=o.scrollHeight,V=Math.abs(I-A);A=I,V<1?x+=1:x=0,o.scrollTop=o.scrollHeight,d.current=o.scrollTop;const q=performance.now()-v;if(x>=3||q>=500){k.current=!1,T.current=null;return}T.current=requestAnimationFrame(M)};T.current=requestAnimationFrame(M)}E.current=!0}},[c,e.length]),r.useEffect(()=>{e.length===0&&(E.current=!1,k.current=!1,T.current!==null&&(cancelAnimationFrame(T.current),T.current=null))},[e.length]);const O=r.useCallback((o,v)=>{var te;const x=u.current;if(!x||h.current.get(o)===v)return;h.current.set(o,v);const M=f.current.get(o);if(M===void 0)return;const I=x.getSize(M),V=v-I,q=n.current;q&&!k.current&&V!==0&&x.getOffset(M)+I+(((te=S.current)==null?void 0:te.offsetTop)??0)<q.scrollTop&&(j.current+=V,P()),x.setSize(M,v)&&g()},[P,g]),W=r.useCallback((o,v="auto")=>{var x;(x=n.current)==null||x.scrollTo({top:o,behavior:v})},[]),Y=me(n,u,S,{reconcile:!0}),U=r.useCallback(()=>{var q;const o=n.current,v=u.current;if(!o||!v||v.count===0)return null;const x=((q=S.current)==null?void 0:q.offsetTop)??0,A=Math.max(0,o.scrollTop-x),M=v.getOffsets(),I=re(M,A);return{key:y.current[I]??null,offsetWithinItem:A-v.getOffset(I),scrollTop:o.scrollTop,scrollHeight:o.scrollHeight}},[]),D=r.useCallback((o,v)=>{var I;const x=u.current;if(!x)return null;const A=f.current.get(o);return A===void 0?null:(((I=S.current)==null?void 0:I.offsetTop)??0)+x.getOffset(A)+v},[]),B=u.current,ce=B?B.totalSize():0,J=n.current,X=(J==null?void 0:J.scrollTop)??d.current,Q=(J==null?void 0:J.clientHeight)??w.current,le=((ee=S.current)==null?void 0:ee.offsetTop)??0,Z=Math.max(0,X-le),$=[];if(B&&B.count>0&&Q>0){const o=B.getOffsets(),v=B.getSizes(),x=re(o,Z),A=he(o,v,Z+Q),M=de({firstVisible:x,lastVisible:A,itemCount:B.count,overscan:i});for(let I=M.start;I<=M.end&&I<e.length;I++)$.push({key:y.current[I]??t(e[I],I),index:I,start:B.getOffset(I),size:B.getSize(I),data:e[I]})}else if(B&&B.count>0){const o=Math.min(e.length,i*2+1),v=c==="bottom"?Math.max(0,e.length-o):0,x=v+o-1;for(let A=v;A<=x;A++)$.push({key:y.current[A]??t(e[A],A),index:A,start:B.getOffset(A),size:B.getSize(A),data:e[A]})}const ie=J?J.scrollHeight-J.scrollTop-J.clientHeight:1/0;return{scrollerRef:n,innerRef:S,virtualItems:$,totalSize:ce,measureItem:O,scrollToIndex:Y,scrollToOffset:W,captureAnchorSnapshot:U,resolveAnchorTop:D,isAtTop:X<=1,isAtBottom:ie<=1,scrollTop:X}}function ge(l){const{scrollerRef:e,itemCount:t,captureAnchorSnapshot:s,resolveAnchorTop:i,onRestored:c}=l,n=r.useRef(null),S=r.useRef(!1),u=r.useRef({first:null,second:null,timeout:null}),h=r.useCallback(()=>{const{first:f,second:m,timeout:F}=u.current;f&&cancelAnimationFrame(f),m&&cancelAnimationFrame(m),F&&clearTimeout(F),u.current={first:null,second:null,timeout:null}},[]),y=r.useCallback(()=>{const f=e.current;if(!f)return;const m=s();n.current=m??{key:null,offsetWithinItem:0,scrollTop:f.scrollTop,scrollHeight:f.scrollHeight},S.current=!0},[e,s]);return r.useLayoutEffect(()=>{if(!S.current)return;const f=e.current,m=n.current;if(!f||!m)return;S.current=!1;const F=()=>{let b=null;m.key!==null&&(b=i(m.key,m.offsetWithinItem)),b===null&&(b=m.scrollTop+(f.scrollHeight-m.scrollHeight)),Number.isFinite(b)&&Math.abs(f.scrollTop-b)>1&&(f.scrollTop=b)};return h(),F(),c==null||c(),u.current.first=requestAnimationFrame(()=>{u.current.first=null,F(),u.current.second=requestAnimationFrame(()=>{u.current.second=null,F()})}),u.current.timeout=setTimeout(()=>{u.current.timeout=null,F()},90),()=>h()},[t,e,i,h,c]),{prepareAnchor:y}}function pe(l){const{previous:e,distanceFromBottom:t,threshold:s,hysteresis:i}=l;if(!i)return t<=s;const c=Math.max(0,i.enter),n=Math.max(c,i.leave);return e?t<=n:t<=c}function ve(l,e){const t=typeof e=="number"?e:e.threshold??200,s=typeof e=="number"?void 0:e.hysteresis,[i,c]=r.useState(!0),n=r.useRef(null),S=r.useRef(!0);return r.useEffect(()=>{const u=l.current;if(!u)return;const h=()=>{const f=u.scrollHeight-u.scrollTop-u.clientHeight,m=pe({previous:S.current,distanceFromBottom:f,threshold:t,hysteresis:s});S.current=m,c(m)},y=()=>{n.current!==null&&cancelAnimationFrame(n.current),n.current=requestAnimationFrame(h)};return u.addEventListener("scroll",y,{passive:!0}),h(),()=>{u.removeEventListener("scroll",y),n.current!==null&&cancelAnimationFrame(n.current)}},[l,t,s==null?void 0:s.enter,s==null?void 0:s.leave]),i}function Re(l){const{itemCount:e,firstKey:t,lastKey:s,isAtBottom:i,scrollToIndex:c,mode:n}=l,S=r.useRef(e),u=r.useRef(t),h=r.useRef(s);r.useLayoutEffect(()=>{if(!n){S.current=e,u.current=t,h.current=s;return}const y=S.current,f=u.current,m=h.current;e>y&&t===f&&s!==m&&i&&e>0&&c(e-1,{align:"end",behavior:n==="smooth"?"smooth":"auto"}),S.current=e,u.current=t,h.current=s},[e,t,s,i,c,n])}function se(l){const{items:e,getKey:t,estimatedItemSize:s=80,overscan:i=20,atBottomThreshold:c=200,atBottomHysteresis:n,followOutput:S="auto",initialAlignment:u="bottom",scrollModifier:h=null,onStartReached:y,onEndReached:f,startReachedThreshold:m=300,endReachedThreshold:F=300,scrollToMessageKey:b,onScrollToMessageComplete:E}=l,d=ne({items:e,getKey:t,estimatedItemSize:s,overscan:i,initialAlignment:u}),w=ve(d.scrollerRef,{threshold:c,hysteresis:n??{enter:80,leave:160}}),[,R]=r.useState(0),k=r.useCallback(()=>R(p=>p+1),[]),{prepareAnchor:T}=ge({scrollerRef:d.scrollerRef,itemCount:e.length,captureAnchorSnapshot:d.captureAnchorSnapshot,resolveAnchorTop:d.resolveAnchorTop,onRestored:k}),j=e.length>0?t(e[0],0):null,_=e.length>0?t(e[e.length-1],e.length-1):null;Re({itemCount:e.length,firstKey:j,lastKey:_,isAtBottom:w,scrollToIndex:d.scrollToIndex,mode:S??!1});const L=r.useCallback((p="auto")=>{e.length!==0&&d.scrollToIndex(e.length-1,{align:"end",behavior:p})},[e.length,d]),K=r.useCallback((p,P)=>{const O=e.findIndex((W,Y)=>t(W,Y)===p);O!==-1&&d.scrollToIndex(O,P)},[e,t,d]),H=r.useRef(null);r.useLayoutEffect(()=>{if(h&&H.current!==h.id){if(H.current=h.id,h.type==="prepend"){T();return}if(h.type==="append"){if(h.ifAtBottomOnly&&!w)return;L(h.behavior??"auto");return}if(h.type==="items-change"){w&&L("auto");return}K(h.key,{align:h.align??"center",behavior:h.behavior??"auto"})}},[h,w,T,L,K]);const a=r.useRef(!1),g=r.useRef(u==="top");r.useEffect(()=>{const p=d.scrollerRef.current;if(!p||!y)return;const P=()=>{const O=p.scrollTop;g.current||(p.scrollHeight<=p.clientHeight+m||O>m)&&(g.current=!0),g.current&&O<=m&&!a.current&&(a.current=!0,Promise.resolve(y()).finally(()=>{a.current=!1}))};return p.addEventListener("scroll",P,{passive:!0}),P(),()=>p.removeEventListener("scroll",P)},[d.scrollerRef,y,m,u]);const z=r.useRef(!1);r.useEffect(()=>{const p=d.scrollerRef.current;if(!p||!f)return;const P=()=>{p.scrollHeight-p.scrollTop-p.clientHeight<=F&&!z.current&&(z.current=!0,Promise.resolve(f()).finally(()=>{z.current=!1}))};return p.addEventListener("scroll",P,{passive:!0}),()=>p.removeEventListener("scroll",P)},[d.scrollerRef,f,F]);const C=r.useRef(null);return r.useEffect(()=>{if(!b||C.current===b)return;const p=e.findIndex((P,O)=>t(P,O)===b);p!==-1&&(C.current=b,d.scrollToIndex(p,{align:"center",behavior:"auto"}),E==null||E())},[b,e,t,d,E]),{scrollerRef:d.scrollerRef,innerRef:d.innerRef,virtualItems:d.virtualItems,totalSize:d.totalSize,measureItem:d.measureItem,scrollToIndex:d.scrollToIndex,scrollToBottom:L,scrollToKey:K,isAtBottom:w,prepareAnchor:T}}function oe({virtualItem:l,measureItem:e,children:t}){const s=r.useRef(null),i=r.useRef(!1);return r.useEffect(()=>{const c=s.current;if(!c)return;i.current=!1;const n=new ResizeObserver(([S])=>{S&&(i.current=!0,e(l.key,S.contentRect.height))});return n.observe(c),()=>n.disconnect()},[l.key,e]),N.jsx("div",{ref:s,style:{position:"absolute",top:0,transform:`translateY(${l.start}px)`,width:"100%",minHeight:i.current?void 0:l.size},children:t})}function ye(l,e){const{data:t,itemContent:s,computeItemKey:i,estimatedItemSize:c=80,overscan:n=20,followOutput:S="auto",atBottomThreshold:u=200,atBottomHysteresis:h,initialAlignment:y="bottom",scrollModifier:f,onStartReached:m,onEndReached:F,startReachedThreshold:b=300,endReachedThreshold:E=300,scrollToMessageKey:d,onScrollToMessageComplete:w,onAtBottomChange:R,components:k={},className:T,style:j}=l,{scrollerRef:_,innerRef:L,virtualItems:K,totalSize:H,measureItem:a,scrollToIndex:g,scrollToBottom:z,scrollToKey:C,isAtBottom:p,prepareAnchor:P}=se({items:t,getKey:(D,B)=>i(B,D),estimatedItemSize:c,overscan:n,atBottomThreshold:u,atBottomHysteresis:h,followOutput:S,initialAlignment:y,scrollModifier:f,onStartReached:m,onEndReached:F,startReachedThreshold:b,endReachedThreshold:E,scrollToMessageKey:d,onScrollToMessageComplete:w}),O=G.useRef(p);G.useEffect(()=>{O.current!==p&&(O.current=p,R==null||R(p))},[p,R]),r.useImperativeHandle(e,()=>({scrollToBottom:z,scrollToIndex:g,scrollToKey:C,getScrollTop:()=>{var D;return((D=_.current)==null?void 0:D.scrollTop)??0},isAtBottom:()=>p,prepareAnchor:P}),[z,g,C,_,p,P]);const{Header:W,Footer:Y,EmptyPlaceholder:U}=k;return t.length===0&&U?N.jsx(U,{}):N.jsxs("div",{ref:_,className:T,style:{overflow:"auto",height:"100%",position:"relative",overscrollBehaviorY:"contain",...j},children:[W&&N.jsx(W,{}),N.jsx("div",{ref:L,style:{height:H,position:"relative",width:"100%"},children:K.map(D=>N.jsx(oe,{virtualItem:D,measureItem:a,children:s(D.index,D.data)},D.key))}),Y&&N.jsx(Y,{})]})}const Te=r.forwardRef(ye);function Se({data:l,itemContent:e,computeItemKey:t,estimatedItemSize:s=60,overscan:i=20,onEndReached:c,endReachedThreshold:n=300,components:S={},className:u,style:h}){const{scrollerRef:y,innerRef:f,virtualItems:m,totalSize:F,measureItem:b}=ne({items:l,getKey:(R,k)=>t(k,R),estimatedItemSize:s,overscan:i,initialAlignment:"top"});G.useEffect(()=>{const R=y.current;if(!R||!c)return;let k=!1;const T=()=>{R.scrollHeight-R.scrollTop-R.clientHeight<=n&&!k&&(k=!0,Promise.resolve(c()).finally(()=>{k=!1}))};return R.addEventListener("scroll",T,{passive:!0}),()=>R.removeEventListener("scroll",T)},[y,c,n]);const{Header:E,Footer:d,EmptyPlaceholder:w}=S;return l.length===0&&w?N.jsx(w,{}):N.jsxs("div",{ref:y,className:u,style:{overflow:"auto",height:"100%",position:"relative",overscrollBehaviorY:"contain",...h},children:[E&&N.jsx(E,{}),N.jsx("div",{ref:f,style:{height:F,position:"relative",width:"100%"},children:m.map(R=>N.jsx(oe,{virtualItem:R,measureItem:b,children:e(R.index,R.data)},R.key))}),d&&N.jsx(d,{})]})}function be(l){const{fetcher:e,initialPage:t=1,direction:s="append",getKey:i,onPageLoaded:c,onError:n}=l,[S,u]=r.useState([]),[h,y]=r.useState(t),[f,m]=r.useState(!0),[F,b]=r.useState(!1),[E,d]=r.useState(!1),[w,R]=r.useState(!1),k=r.useRef(new Set),T=r.useRef(!1),j=r.useCallback(a=>i?a.filter(g=>{const z=i(g);return k.current.has(z)?!1:(k.current.add(z),!0)}):a,[i]),_=r.useCallback(async()=>{if(!(T.current||!f)){T.current=!0,R(!0);try{const a=h+1,g=await e(a),z=j(g.data);u(C=>s==="prepend"?[...z,...C]:[...C,...z]),y(a),m(g.hasNextPage),b(g.hasPrevPage),c==null||c(a,z)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{R(!1),T.current=!1}}},[h,f,e,j,s,c,n]),L=r.useCallback(async()=>{if(!(T.current||!F)){T.current=!0,R(!0);try{const a=h-1,g=await e(a),z=j(g.data);u(C=>[...z,...C]),y(a),b(g.hasPrevPage),m(g.hasNextPage),c==null||c(a,z)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{R(!1),T.current=!1}}},[h,F,e,j,c,n]),K=r.useCallback(async()=>{if(!T.current){T.current=!0,d(!0);try{const a=await e(t),g=a.data;if(i){const z=new Set(g.map(i));g.forEach(C=>k.current.add(i(C))),u(C=>{const p=C.filter(P=>!z.has(i(P)));return s==="prepend"?[...g,...p]:[...p,...g]})}else u(g);y(t),m(a.hasNextPage),b(a.hasPrevPage),c==null||c(t,g)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{d(!1),T.current=!1}}},[e,t,i,s,c,n]),H=r.useCallback(()=>{u([]),y(t),m(!0),b(!1),d(!1),R(!1),k.current.clear(),T.current=!1},[t]);return{items:S,loadNextPage:_,loadPrevPage:L,hasNextPage:f,hasPrevPage:F,loading:E,loadingMore:w,refresh:K,reset:H,currentPage:h}}exports.ChatVirtualList=Te;exports.VirtualList=Se;exports.useChatVirtualizer=se;exports.usePagination=be;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("react/jsx-runtime"),r=require("react");function fe(i){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const t in i)if(t!=="default"){const s=Object.getOwnPropertyDescriptor(i,t);Object.defineProperty(e,t,s.get?s:{enumerable:!0,get:()=>i[t]})}}return e.default=i,Object.freeze(e)}const ee=fe(r);class he{constructor(e,t){this.defaultSize=t,this.sizes=e>0?Array(e).fill(t):[],this.offsets=e>0?Array(e).fill(0):[],e>0&&this._recalcFrom(0)}_recalcFrom(e){for(let t=e;t<this.sizes.length;t++)this.offsets[t]=t===0?0:(this.offsets[t-1]??0)+(this.sizes[t-1]??this.defaultSize)}getOffset(e){return this.offsets[e]??0}getSize(e){return this.sizes[e]??this.defaultSize}setSize(e,t){return this.sizes[e]===t?!1:(this.sizes[e]=t,this._recalcFrom(e+1),!0)}prepend(e){const t=Array(e).fill(this.defaultSize);this.sizes=[...t,...this.sizes],this.offsets=Array(this.sizes.length).fill(0),this._recalcFrom(0)}append(e){const t=this.sizes.length;for(let s=0;s<e;s++)this.sizes.push(this.defaultSize),this.offsets.push(0);this._recalcFrom(t)}resize(e){const t=this.sizes.length;e>t?this.append(e-t):e<t&&(this.sizes=this.sizes.slice(0,e),this.offsets=this.offsets.slice(0,e))}totalSize(){if(this.sizes.length===0)return 0;const e=this.sizes.length-1;return(this.offsets[e]??0)+(this.sizes[e]??this.defaultSize)}get count(){return this.sizes.length}getOffsets(){return this.offsets}getSizes(){return this.sizes}}class de{constructor(){this.cache=new Map}get(e){return this.cache.get(e)}set(e,t){this.cache.set(e,t)}has(e){return this.cache.has(e)}delete(e){this.cache.delete(e)}clear(){this.cache.clear()}applyToOffsetMap(e,t){for(const[s,l]of this.cache){const o=t.get(s);o!==void 0&&e.setSize(o,l)}}}function se(i,e){if(i.length===0)return 0;let t=0,s=i.length-1;for(;t<s;){const l=t+s>>1;(i[l]??0)<e?t=l+1:s=l}return Math.max(0,t>0&&(i[t]??0)>e?t-1:t)}function me(i,e,t){if(i.length===0)return 0;let s=0,l=i.length-1,o=0;for(;s<=l;){const n=s+l>>1;(i[n]??0)<t?(o=n,s=n+1):l=n-1}return o}function ge(i){const{firstVisible:e,lastVisible:t,itemCount:s,overscan:l}=i;return s===0?{start:0,end:-1}:{start:Math.max(0,e-l),end:Math.min(s-1,t+l)}}function pe(i,e,t,s){const l=s==null?void 0:s.reconcile,o=r.useRef(null);return r.useCallback((n,S)=>{const u=i.current,h=e.current;if(!u||!h)return;o.current!==null&&(cancelAnimationFrame(o.current),o.current=null);let y,f;if(typeof n=="object"&&n!==null?(y=n.index,f={align:n.align,behavior:n.behavior,offset:n.offset}):(y=n,f=S),!Number.isFinite(y))return;const v=Math.max(0,Math.min(Math.floor(y),h.count-1));h.getOffset(v),h.getSize(v);const I=(f==null?void 0:f.align)??"start",F=(f==null?void 0:f.behavior)??"auto",E=(f==null?void 0:f.offset)??0,d=()=>{var p;const H=e.current,a=i.current;if(!H||!a)return 0;const m=((p=t==null?void 0:t.current)==null?void 0:p.offsetTop)??0,z=H.getOffset(v),M=H.getSize(v);return I==="start"?m+z+E:I==="center"?m+z-a.clientHeight/2+M/2+E:m+z-a.clientHeight+M+E},w=d();((H,a)=>{typeof u.scrollTo=="function"?u.scrollTo({top:H,behavior:a}):u.scrollTop=H})(Math.max(0,w),F);const k=performance.now();let T=0;const j=12,_=300,O=1,K=()=>{o.current=null;const H=i.current;if(!H)return;const a=Math.max(0,d()),m=Math.abs(H.scrollTop-a),z=performance.now()-k>=_,M=T>=j;m<=O||z||M||(typeof H.scrollTo=="function"?H.scrollTo({top:a,behavior:"auto"}):H.scrollTop=a,T+=1,o.current=requestAnimationFrame(K))};o.current=requestAnimationFrame(K)},[i,e,t,l])}function oe(i){var ne;const{items:e,getKey:t,estimatedItemSize:s,overscan:l,initialAlignment:o}=i,n=r.useRef(null),S=r.useRef(null),u=r.useRef(null),h=r.useRef(new de),y=r.useRef([]),f=r.useRef(new Map),v=r.useRef(e.length),I=r.useRef(null),F=r.useRef(null),E=r.useRef(!1),d=r.useRef(0),w=r.useRef(0),R=r.useRef(null),k=r.useRef(!1),T=r.useRef(null),j=r.useRef(0),_=r.useRef(0),O=r.useRef(0),K=r.useRef(null),H=r.useRef(!1),[,a]=r.useState(0),m=r.useCallback(()=>a(c=>c+1),[]);u.current||(u.current=new he(e.length,s));const z=e.length>0?t(e[0],0):null,M=e.length>0?t(e[e.length-1],e.length-1):null;if(e.length!==v.current||z!==I.current||M!==F.current){const c=u.current,g=e.map((L,q)=>t(L,q)),A=y.current,x=A.length,C=g.length;if(C===0)c.resize(0);else if(x===0)c.resize(C);else if(C>x){const L=C-x;A.length>0&&L>=0&&g[L]===A[0]?c.prepend(L):c.resize(C)}else C<x,c.resize(C);const b=new Map;g.forEach((L,q)=>b.set(L,q)),f.current=b,h.current.applyToOffsetMap(c,b),y.current=g,v.current=e.length,I.current=z,F.current=M}const P=r.useCallback(()=>{K.current===null&&(K.current=requestAnimationFrame(()=>{K.current=null;const c=n.current;if(!c)return;const g=j.current;Math.abs(g)<.01||(j.current=0,_.current+=g,O.current+=g,c.scrollTop+=g,d.current=c.scrollTop,H.current=!0,m())}))},[m]);r.useEffect(()=>{const c=n.current;if(!c)return;const g=()=>{R.current===null&&(R.current=requestAnimationFrame(()=>{R.current=null,d.current=c.scrollTop,w.current=c.clientHeight,H.current&&(H.current=!1,_.current=0,O.current=0),m()}))};return c.addEventListener("scroll",g,{passive:!0}),()=>{c.removeEventListener("scroll",g),R.current!==null&&(cancelAnimationFrame(R.current),R.current=null),K.current!==null&&(cancelAnimationFrame(K.current),K.current=null)}},[m]),r.useEffect(()=>{const c=n.current;if(!c)return;w.current=c.clientHeight,d.current=c.scrollTop;const g=new ResizeObserver(([A])=>{A&&(w.current=A.contentRect.height,m())});return g.observe(c),()=>g.disconnect()},[m]),r.useLayoutEffect(()=>{if(E.current||e.length===0)return;const c=n.current;if(c){if(o==="bottom"){c.scrollTop=c.scrollHeight,d.current=c.scrollTop,w.current=c.clientHeight,k.current=!0,T.current!==null&&cancelAnimationFrame(T.current);const g=performance.now();let A=0,x=c.scrollHeight;const C=()=>{if(!k.current){T.current=null;return}const b=c.scrollHeight,L=Math.abs(b-x);x=b,L<1?A+=1:A=0,c.scrollTop=c.scrollHeight,d.current=c.scrollTop;const q=performance.now()-g;if(A>=3||q>=500){k.current=!1,T.current=null;return}T.current=requestAnimationFrame(C)};T.current=requestAnimationFrame(C)}E.current=!0}},[o,e.length]),r.useEffect(()=>{e.length===0&&(E.current=!1,k.current=!1,T.current!==null&&(cancelAnimationFrame(T.current),T.current=null))},[e.length]);const V=r.useCallback((c,g)=>{var Y;const A=u.current;if(!A||h.current.get(c)===g)return;h.current.set(c,g);const C=f.current.get(c);if(C===void 0)return;const b=A.getSize(C),L=g-b,q=n.current;q&&!k.current&&L!==0&&A.getOffset(C)+b+(((Y=S.current)==null?void 0:Y.offsetTop)??0)<q.scrollTop&&(j.current+=L,P()),A.setSize(C,g)&&m()},[P,m]),J=r.useCallback((c,g="auto")=>{var A;(A=n.current)==null||A.scrollTo({top:c,behavior:g})},[]),U=pe(n,u,S,{reconcile:!0}),X=r.useCallback(()=>{var Q;const c=n.current,g=u.current;if(!c||!g||g.count===0)return null;const A=((Q=S.current)==null?void 0:Q.offsetTop)??0,x=Math.max(0,c.scrollTop-A),C=g.getOffsets(),b=se(C,x),L=y.current[b]??null,q=[];for(let Y=b;Y<Math.min(g.count,b+6);Y++){const Z=y.current[Y]??null;Z!==null&&q.push({key:Z,offsetWithinItem:x-g.getOffset(Y)})}return{key:L,offsetWithinItem:x-g.getOffset(b),candidates:q,scrollTop:c.scrollTop,scrollHeight:c.scrollHeight}},[]),W=r.useCallback((c,g)=>{var b;const A=u.current;if(!A)return null;const x=f.current.get(c);return x===void 0?null:(((b=S.current)==null?void 0:b.offsetTop)??0)+A.getOffset(x)+g},[]),B=u.current,ie=B?B.totalSize():0,D=n.current,$=(D==null?void 0:D.scrollTop)??d.current,te=(D==null?void 0:D.clientHeight)??w.current,ue=((ne=S.current)==null?void 0:ne.offsetTop)??0,re=Math.max(0,$-ue),G=[];if(B&&B.count>0&&te>0){const c=B.getOffsets(),g=B.getSizes(),A=se(c,re),x=me(c,g,re+te),C=ge({firstVisible:A,lastVisible:x,itemCount:B.count,overscan:l});for(let b=C.start;b<=C.end&&b<e.length;b++)G.push({key:y.current[b]??t(e[b],b),index:b,start:B.getOffset(b),size:B.getSize(b),data:e[b]})}else if(B&&B.count>0){const c=Math.min(e.length,l*2+1),g=o==="bottom"?Math.max(0,e.length-c):0,A=g+c-1;for(let x=g;x<=A;x++)G.push({key:y.current[x]??t(e[x],x),index:x,start:B.getOffset(x),size:B.getSize(x),data:e[x]})}const ae=D?D.scrollHeight-D.scrollTop-D.clientHeight:1/0;return{scrollerRef:n,innerRef:S,virtualItems:G,totalSize:ie,measureItem:V,scrollToIndex:U,scrollToOffset:J,captureAnchorSnapshot:X,resolveAnchorTop:W,isAtTop:$<=1,isAtBottom:ae<=1,scrollTop:$}}function ve(i){var l;const{snapshot:e,currentScrollHeight:t,resolveAnchorTop:s}=i;if(e.key!==null){const o=s(e.key,e.offsetWithinItem);if(o!==null)return o}if((l=e.candidates)!=null&&l.length)for(const o of e.candidates){const n=s(o.key,o.offsetWithinItem);if(n!==null)return n}return e.scrollTop+(t-e.scrollHeight)}function Re(i){const{scrollerRef:e,itemCount:t,captureAnchorSnapshot:s,resolveAnchorTop:l,onRestored:o}=i,n=r.useRef(null),S=r.useRef(!1),u=r.useRef({first:null,second:null,timeout:null}),h=r.useCallback(()=>{const{first:f,second:v,timeout:I}=u.current;f&&cancelAnimationFrame(f),v&&cancelAnimationFrame(v),I&&clearTimeout(I),u.current={first:null,second:null,timeout:null}},[]),y=r.useCallback(()=>{const f=e.current;if(!f)return;const v=s();n.current=v??{key:null,offsetWithinItem:0,scrollTop:f.scrollTop,scrollHeight:f.scrollHeight},S.current=!0},[e,s]);return r.useLayoutEffect(()=>{if(!S.current)return;const f=e.current,v=n.current;if(!f||!v)return;S.current=!1;const I=()=>{const F=ve({snapshot:v,currentScrollHeight:f.scrollHeight,resolveAnchorTop:l});Number.isFinite(F)&&Math.abs(f.scrollTop-F)>1&&(f.scrollTop=F)};return h(),I(),o==null||o(),u.current.first=requestAnimationFrame(()=>{u.current.first=null,I(),u.current.second=requestAnimationFrame(()=>{u.current.second=null,I()})}),u.current.timeout=setTimeout(()=>{u.current.timeout=null,I()},90),()=>h()},[t,e,l,h,o]),{prepareAnchor:y}}function ye(i){const{previous:e,distanceFromBottom:t,threshold:s,hysteresis:l}=i;if(!l)return t<=s;const o=Math.max(0,l.enter),n=Math.max(o,l.leave);return e?t<=n:t<=o}function Te(i,e){const t=typeof e=="number"?e:e.threshold??200,s=typeof e=="number"?void 0:e.hysteresis,[l,o]=r.useState(!0),n=r.useRef(null),S=r.useRef(!0);return r.useEffect(()=>{const u=i.current;if(!u)return;const h=()=>{const f=u.scrollHeight-u.scrollTop-u.clientHeight,v=ye({previous:S.current,distanceFromBottom:f,threshold:t,hysteresis:s});S.current=v,o(v)},y=()=>{n.current!==null&&cancelAnimationFrame(n.current),n.current=requestAnimationFrame(h)};return u.addEventListener("scroll",y,{passive:!0}),h(),()=>{u.removeEventListener("scroll",y),n.current!==null&&cancelAnimationFrame(n.current)}},[i,t,s==null?void 0:s.enter,s==null?void 0:s.leave]),l}function Se(i){const{itemCount:e,firstKey:t,lastKey:s,isAtBottom:l,scrollToIndex:o,mode:n}=i,S=r.useRef(e),u=r.useRef(t),h=r.useRef(s);r.useLayoutEffect(()=>{if(!n){S.current=e,u.current=t,h.current=s;return}const y=S.current,f=u.current,v=h.current;e>y&&t===f&&s!==v&&l&&e>0&&o(e-1,{align:"end",behavior:n==="smooth"?"smooth":"auto"}),S.current=e,u.current=t,h.current=s},[e,t,s,l,o,n])}function ce(i){const{items:e,getKey:t,estimatedItemSize:s=80,overscan:l=20,atBottomThreshold:o=200,atBottomHysteresis:n,followOutput:S="auto",initialAlignment:u="bottom",scrollModifier:h=null,onStartReached:y,onEndReached:f,startReachedThreshold:v=300,endReachedThreshold:I=300,scrollToMessageKey:F,onScrollToMessageComplete:E}=i,d=oe({items:e,getKey:t,estimatedItemSize:s,overscan:l,initialAlignment:u}),w=Te(d.scrollerRef,{threshold:o,hysteresis:n??{enter:80,leave:160}}),[,R]=r.useState(0),k=r.useCallback(()=>R(p=>p+1),[]),{prepareAnchor:T}=Re({scrollerRef:d.scrollerRef,itemCount:e.length,captureAnchorSnapshot:d.captureAnchorSnapshot,resolveAnchorTop:d.resolveAnchorTop,onRestored:k}),j=e.length>0?t(e[0],0):null,_=e.length>0?t(e[e.length-1],e.length-1):null;Se({itemCount:e.length,firstKey:j,lastKey:_,isAtBottom:w,scrollToIndex:d.scrollToIndex,mode:S??!1});const O=r.useCallback((p="auto")=>{e.length!==0&&d.scrollToIndex(e.length-1,{align:"end",behavior:p})},[e.length,d]),K=r.useCallback((p,P)=>{const V=e.findIndex((J,U)=>t(J,U)===p);V!==-1&&d.scrollToIndex(V,P)},[e,t,d]),H=r.useRef(null);r.useLayoutEffect(()=>{if(h&&H.current!==h.id){if(H.current=h.id,h.type==="prepend"){T();return}if(h.type==="append"){if(h.ifAtBottomOnly&&!w)return;O(h.behavior??"auto");return}if(h.type==="items-change"){w&&O("auto");return}K(h.key,{align:h.align??"center",behavior:h.behavior??"auto"})}},[h,w,T,O,K]);const a=r.useRef(!1),m=r.useRef(u==="top");r.useEffect(()=>{const p=d.scrollerRef.current;if(!p||!y)return;const P=()=>{const V=p.scrollTop;m.current||(p.scrollHeight<=p.clientHeight+v||V>v)&&(m.current=!0),m.current&&V<=v&&!a.current&&(a.current=!0,Promise.resolve(y()).finally(()=>{a.current=!1}))};return p.addEventListener("scroll",P,{passive:!0}),P(),()=>p.removeEventListener("scroll",P)},[d.scrollerRef,y,v,u]);const z=r.useRef(!1);r.useEffect(()=>{const p=d.scrollerRef.current;if(!p||!f)return;const P=()=>{p.scrollHeight-p.scrollTop-p.clientHeight<=I&&!z.current&&(z.current=!0,Promise.resolve(f()).finally(()=>{z.current=!1}))};return p.addEventListener("scroll",P,{passive:!0}),()=>p.removeEventListener("scroll",P)},[d.scrollerRef,f,I]);const M=r.useRef(null);return r.useEffect(()=>{if(!F||M.current===F)return;const p=e.findIndex((P,V)=>t(P,V)===F);p!==-1&&(M.current=F,d.scrollToIndex(p,{align:"center",behavior:"auto"}),E==null||E())},[F,e,t,d,E]),{scrollerRef:d.scrollerRef,innerRef:d.innerRef,virtualItems:d.virtualItems,totalSize:d.totalSize,measureItem:d.measureItem,scrollToIndex:d.scrollToIndex,scrollToBottom:O,scrollToKey:K,isAtBottom:w,prepareAnchor:T}}function le({virtualItem:i,measureItem:e,children:t}){const s=r.useRef(null),l=r.useRef(!1);return r.useEffect(()=>{const o=s.current;if(!o)return;l.current=!1;const n=new ResizeObserver(([S])=>{S&&(l.current=!0,e(i.key,S.contentRect.height))});return n.observe(o),()=>n.disconnect()},[i.key,e]),N.jsx("div",{ref:s,style:{position:"absolute",top:0,transform:`translateY(${i.start}px)`,width:"100%",minHeight:l.current?void 0:i.size},children:t})}function be(i,e){const{data:t,itemContent:s,computeItemKey:l,estimatedItemSize:o=80,overscan:n=20,followOutput:S="auto",atBottomThreshold:u=200,atBottomHysteresis:h,initialAlignment:y="bottom",scrollModifier:f,onStartReached:v,onEndReached:I,startReachedThreshold:F=300,endReachedThreshold:E=300,scrollToMessageKey:d,onScrollToMessageComplete:w,onAtBottomChange:R,components:k={},className:T,style:j}=i,{scrollerRef:_,innerRef:O,virtualItems:K,totalSize:H,measureItem:a,scrollToIndex:m,scrollToBottom:z,scrollToKey:M,isAtBottom:p,prepareAnchor:P}=ce({items:t,getKey:(W,B)=>l(B,W),estimatedItemSize:o,overscan:n,atBottomThreshold:u,atBottomHysteresis:h,followOutput:S,initialAlignment:y,scrollModifier:f,onStartReached:v,onEndReached:I,startReachedThreshold:F,endReachedThreshold:E,scrollToMessageKey:d,onScrollToMessageComplete:w}),V=ee.useRef(p);ee.useEffect(()=>{V.current!==p&&(V.current=p,R==null||R(p))},[p,R]),r.useImperativeHandle(e,()=>({scrollToBottom:z,scrollToIndex:m,scrollToKey:M,getScrollTop:()=>{var W;return((W=_.current)==null?void 0:W.scrollTop)??0},isAtBottom:()=>p,prepareAnchor:P}),[z,m,M,_,p,P]);const{Header:J,Footer:U,EmptyPlaceholder:X}=k;return t.length===0&&X?N.jsx(X,{}):N.jsxs("div",{ref:_,className:T,style:{overflow:"auto",height:"100%",position:"relative",overflowAnchor:"none",overscrollBehaviorY:"contain",...j},children:[J&&N.jsx(J,{}),N.jsx("div",{ref:O,style:{height:H,position:"relative",width:"100%"},children:K.map(W=>N.jsx(le,{virtualItem:W,measureItem:a,children:s(W.index,W.data)},W.key))}),U&&N.jsx(U,{})]})}const ze=r.forwardRef(be);function Ae({data:i,itemContent:e,computeItemKey:t,estimatedItemSize:s=60,overscan:l=20,onEndReached:o,endReachedThreshold:n=300,components:S={},className:u,style:h}){const{scrollerRef:y,innerRef:f,virtualItems:v,totalSize:I,measureItem:F}=oe({items:i,getKey:(R,k)=>t(k,R),estimatedItemSize:s,overscan:l,initialAlignment:"top"});ee.useEffect(()=>{const R=y.current;if(!R||!o)return;let k=!1;const T=()=>{R.scrollHeight-R.scrollTop-R.clientHeight<=n&&!k&&(k=!0,Promise.resolve(o()).finally(()=>{k=!1}))};return R.addEventListener("scroll",T,{passive:!0}),()=>R.removeEventListener("scroll",T)},[y,o,n]);const{Header:E,Footer:d,EmptyPlaceholder:w}=S;return i.length===0&&w?N.jsx(w,{}):N.jsxs("div",{ref:y,className:u,style:{overflow:"auto",height:"100%",position:"relative",overflowAnchor:"none",overscrollBehaviorY:"contain",...h},children:[E&&N.jsx(E,{}),N.jsx("div",{ref:f,style:{height:I,position:"relative",width:"100%"},children:v.map(R=>N.jsx(le,{virtualItem:R,measureItem:F,children:e(R.index,R.data)},R.key))}),d&&N.jsx(d,{})]})}function xe(i){const{fetcher:e,initialPage:t=1,direction:s="append",getKey:l,onPageLoaded:o,onError:n}=i,[S,u]=r.useState([]),[h,y]=r.useState(t),[f,v]=r.useState(!0),[I,F]=r.useState(!1),[E,d]=r.useState(!1),[w,R]=r.useState(!1),k=r.useRef(new Set),T=r.useRef(!1),j=r.useCallback(a=>l?a.filter(m=>{const z=l(m);return k.current.has(z)?!1:(k.current.add(z),!0)}):a,[l]),_=r.useCallback(async()=>{if(!(T.current||!f)){T.current=!0,R(!0);try{const a=h+1,m=await e(a),z=j(m.data);u(M=>s==="prepend"?[...z,...M]:[...M,...z]),y(a),v(m.hasNextPage),F(m.hasPrevPage),o==null||o(a,z)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{R(!1),T.current=!1}}},[h,f,e,j,s,o,n]),O=r.useCallback(async()=>{if(!(T.current||!I)){T.current=!0,R(!0);try{const a=h-1,m=await e(a),z=j(m.data);u(M=>[...z,...M]),y(a),F(m.hasPrevPage),v(m.hasNextPage),o==null||o(a,z)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{R(!1),T.current=!1}}},[h,I,e,j,o,n]),K=r.useCallback(async()=>{if(!T.current){T.current=!0,d(!0);try{const a=await e(t),m=a.data;if(l){const z=new Set(m.map(l));m.forEach(M=>k.current.add(l(M))),u(M=>{const p=M.filter(P=>!z.has(l(P)));return s==="prepend"?[...m,...p]:[...p,...m]})}else u(m);y(t),v(a.hasNextPage),F(a.hasPrevPage),o==null||o(t,m)}catch(a){n==null||n(a instanceof Error?a:new Error(String(a)))}finally{d(!1),T.current=!1}}},[e,t,l,s,o,n]),H=r.useCallback(()=>{u([]),y(t),v(!0),F(!1),d(!1),R(!1),k.current.clear(),T.current=!1},[t]);return{items:S,loadNextPage:_,loadPrevPage:O,hasNextPage:f,hasPrevPage:I,loading:E,loadingMore:w,refresh:K,reset:H,currentPage:h}}exports.ChatVirtualList=ze;exports.VirtualList=Ae;exports.useChatVirtualizer=ce;exports.usePagination=xe;
|