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 CHANGED
@@ -1,202 +1,129 @@
1
1
  # react-anchorlist
2
2
 
3
- **Lista virtualizada de alta performance para React, pensada para interfaces com muitos itens (como chat).**
3
+ High-performance virtualized lists for React, optimized for chat and infinite feeds.
4
4
 
5
- Sem flicker ao carregar itens anteriores. Scroll fluido. Paginação nativa.
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
- ## O que é
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
- `react-anchorlist` é uma biblioteca para renderizar listas grandes com boa performance.
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 'react-anchorlist'
24
+ import { VirtualList } from "react-anchorlist"
46
25
 
47
26
  <VirtualList
48
27
  data={tickets}
49
- computeItemKey={(index, item) => item.id}
50
- itemContent={(index, item) => <TicketRow ticket={item} />}
28
+ computeItemKey={(_, item) => item.id}
29
+ itemContent={(_, item) => <TicketRow ticket={item} />}
51
30
  onEndReached={loadMore}
52
- style={{ height: '100%' }}
31
+ style={{ height: "100%" }}
53
32
  />
54
33
  ```
55
34
 
56
- ### Lista de chat (mensagens com paginação)
35
+ ### Chat list (recommended pattern)
57
36
 
58
37
  ```tsx
59
- import { ChatVirtualList } from 'react-anchorlist'
60
- import type { ChatVirtualListHandle } from 'react-anchorlist'
38
+ import { useState } from "react"
39
+ import { ChatVirtualList } from "react-anchorlist"
40
+ import type { ChatScrollModifier } from "react-anchorlist"
61
41
 
62
- const listRef = useRef<ChatVirtualListHandle>(null)
42
+ const [scrollModifier, setScrollModifier] = useState<ChatScrollModifier | null>(null)
63
43
 
64
44
  <ChatVirtualList
65
- ref={listRef}
66
45
  data={messages}
67
- computeItemKey={(index, item) => item._id}
68
- itemContent={(index, item) => <Message data={item} />}
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
- // Informa ao componente pai se está no final
79
- onAtBottomChange={setIsAtBottom}
80
- components={{
81
- Header: () => loading ? <Spinner /> : null,
82
- Footer: () => <QueueMessages />,
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
- style={{ height: '100%' }}
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
- ```tsx
137
- const listRef = useRef<ChatVirtualListHandle>(null)
63
+ When data changes, control scroll behavior with `scrollModifier`:
138
64
 
139
- listRef.current?.scrollToBottom()
140
- listRef.current?.scrollToIndex(42, { align: 'center', behavior: 'smooth' })
141
- listRef.current?.scrollToKey('msg-123', { align: 'center' })
142
- listRef.current?.getScrollTop() // number
143
- listRef.current?.isAtBottom() // boolean
144
- listRef.current?.prepareAnchor() // deprecated
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
- | Method | Description |
148
- |---|---|
149
- | `scrollToBottom(behavior?)` | Vai para o último item |
150
- | `scrollToIndex(index, opts?)` | Vai para item por índice |
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
- ## ChatScrollModifier (API declarativa)
80
+ ### Exports
159
81
 
160
82
  ```ts
161
- type ChatScrollModifier =
162
- | { id: string | number; type: 'prepend' }
163
- | { id: string | number; type: 'append'; behavior?: 'auto' | 'smooth'; ifAtBottomOnly?: boolean }
164
- | { id: string | number; type: 'items-change' }
165
- | { id: string | number; type: 'jump-to-key'; key: string | number; align?: 'start' | 'center' | 'end'; behavior?: ScrollBehavior }
83
+ import {
84
+ ChatVirtualList,
85
+ VirtualList,
86
+ useChatVirtualizer,
87
+ usePagination,
88
+ } from "react-anchorlist"
166
89
  ```
167
90
 
168
- - `id` precisa ser único por comando.
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
- ## Migração Rápida (v0.2 -> v0.3)
102
+ ### `VirtualList` most-used props
175
103
 
176
- Antes:
104
+ - `data`, `itemContent`, `computeItemKey` (required)
105
+ - `onEndReached`
106
+ - `endReachedThreshold` (default: `300`)
107
+ - `estimatedItemSize` (default: `60`)
108
+ - `overscan` (default: `20`)
177
109
 
178
- ```tsx
179
- listRef.current?.prepareAnchor()
180
- await loadMoreMessages()
181
- ```
182
-
183
- Depois:
110
+ ### `ChatVirtualList` ref handle
184
111
 
185
112
  ```tsx
186
- setScrollModifier({ id: `prepend-${Date.now()}`, type: 'prepend' })
187
- await loadMoreMessages()
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 { usePagination, ChatVirtualList } from 'react-anchorlist'
123
+ import { useEffect } from "react"
124
+ import { usePagination, ChatVirtualList } from "react-anchorlist"
198
125
 
199
- const { items, loadPrevPage, hasPrevPage, loadingMore } = usePagination({
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: 'prepend', // novas páginas entram no topo
210
- getKey: (msg) => msg._id, // chave para deduplicação
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
- ## Boas práticas
227
-
228
- - Use **chave estável** em `computeItemKey`
229
- - Evite lógica pesada em `itemContent`
230
- - Padronize paginação e ordenação no back-end
231
- - Ajuste `estimatedItemSize` para o tipo de item predominante
232
- - Mantenha `overscan` baixo e só aumente se houver necessidade real
233
-
234
- ---
235
-
236
- ## Como funciona internamente
237
-
238
- - **OffsetMap:** calcula offsets acumulados por item
239
- - **Âncora de scroll:** preserva posição ao prepend
240
- - **ResizeObserver por item:** mede altura real ao renderizar
241
- - **Busca binária:** encontra faixa visível com eficiência
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;AAsHxF,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
+ {"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,2CAyDrB"}
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,CAyF9F"}
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,CAqW5B"}
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;