@indielayer/ui 1.16.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/docs/components/menu/DocsMenu.vue +3 -0
  2. package/docs/pages/component/infiniteLoader/composable.vue +168 -0
  3. package/docs/pages/component/infiniteLoader/index.vue +36 -0
  4. package/docs/pages/component/infiniteLoader/usage.vue +161 -0
  5. package/docs/pages/component/virtualGrid/index.vue +29 -0
  6. package/docs/pages/component/virtualGrid/usage.vue +20 -0
  7. package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
  8. package/docs/pages/component/virtualList/index.vue +36 -0
  9. package/docs/pages/component/virtualList/usage.vue +17 -0
  10. package/docs/search/components.json +1 -1
  11. package/lib/components/select/Select.vue.js +35 -35
  12. package/lib/components/table/Table.vue.js +1 -1
  13. package/lib/composables/useVirtualList.d.ts +1 -1
  14. package/lib/index.d.ts +1 -0
  15. package/lib/index.js +88 -76
  16. package/lib/index.umd.js +4 -4
  17. package/lib/install.js +15 -7
  18. package/lib/version.d.ts +1 -1
  19. package/lib/version.js +1 -1
  20. package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
  21. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
  22. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
  23. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
  24. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
  25. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
  26. package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
  27. package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
  28. package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
  29. package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
  30. package/lib/virtual/components/virtualList/VirtualList.vue.js +157 -0
  31. package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
  32. package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
  33. package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
  34. package/lib/virtual/components/virtualList/types.d.ts +115 -0
  35. package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
  36. package/lib/virtual/components/virtualList/useDynamicRowHeight.js +69 -0
  37. package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
  38. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
  39. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
  40. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
  41. package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
  42. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
  43. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
  44. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
  45. package/lib/virtual/core/createCachedBounds.d.ts +6 -0
  46. package/lib/virtual/core/createCachedBounds.js +55 -0
  47. package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
  48. package/lib/virtual/core/getEstimatedSize.js +22 -0
  49. package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
  50. package/lib/virtual/core/getOffsetForIndex.js +40 -0
  51. package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
  52. package/lib/virtual/core/getStartStopIndices.js +31 -0
  53. package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
  54. package/lib/virtual/core/types.d.ts +11 -0
  55. package/lib/virtual/core/useCachedBounds.d.ts +7 -0
  56. package/lib/virtual/core/useCachedBounds.js +18 -0
  57. package/lib/virtual/core/useIsRtl.d.ts +2 -0
  58. package/lib/virtual/core/useIsRtl.js +15 -0
  59. package/lib/virtual/core/useItemSize.d.ts +5 -0
  60. package/lib/virtual/core/useItemSize.js +27 -0
  61. package/lib/virtual/core/useVirtualizer.d.ts +33 -0
  62. package/lib/virtual/core/useVirtualizer.js +171 -0
  63. package/lib/virtual/index.d.ts +9 -0
  64. package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
  65. package/lib/virtual/types.d.ts +2 -0
  66. package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
  67. package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
  68. package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
  69. package/lib/virtual/utils/assert.d.ts +1 -0
  70. package/lib/virtual/utils/assert.js +7 -0
  71. package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
  72. package/lib/virtual/utils/getRTLOffsetType.js +13 -0
  73. package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
  74. package/lib/virtual/utils/getScrollbarSize.js +11 -0
  75. package/lib/virtual/utils/isRtl.d.ts +1 -0
  76. package/lib/virtual/utils/isRtl.js +12 -0
  77. package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
  78. package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
  79. package/lib/virtual/utils/shallowCompare.d.ts +1 -0
  80. package/lib/virtual/utils/shallowCompare.js +14 -0
  81. package/package.json +1 -1
  82. package/src/components/select/Select.vue +3 -2
  83. package/src/components/table/Table.vue +1 -1
  84. package/src/composables/useVirtualList.ts +1 -1
  85. package/src/index.ts +1 -0
  86. package/src/install.ts +9 -3
  87. package/src/version.ts +1 -1
  88. package/src/virtual/README.md +285 -0
  89. package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
  90. package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
  91. package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
  92. package/src/virtual/components/virtualGrid/types.ts +160 -0
  93. package/src/virtual/components/virtualList/VirtualList.test.ts +47 -0
  94. package/src/virtual/components/virtualList/VirtualList.vue +233 -0
  95. package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
  96. package/src/virtual/components/virtualList/types.ts +127 -0
  97. package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +183 -0
  98. package/src/virtual/components/virtualList/useDynamicRowHeight.ts +147 -0
  99. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
  100. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
  101. package/src/virtual/composables/infinite-loader/types.ts +36 -0
  102. package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
  103. package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
  104. package/src/virtual/core/createCachedBounds.ts +72 -0
  105. package/src/virtual/core/getEstimatedSize.ts +29 -0
  106. package/src/virtual/core/getOffsetForIndex.ts +90 -0
  107. package/src/virtual/core/getStartStopIndices.test.ts +45 -0
  108. package/src/virtual/core/getStartStopIndices.ts +71 -0
  109. package/src/virtual/core/types.ts +17 -0
  110. package/src/virtual/core/useCachedBounds.ts +21 -0
  111. package/src/virtual/core/useIsRtl.ts +25 -0
  112. package/src/virtual/core/useItemSize.ts +34 -0
  113. package/src/virtual/core/useVirtualizer.ts +294 -0
  114. package/src/virtual/index.ts +25 -0
  115. package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
  116. package/src/virtual/types.ts +3 -0
  117. package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
  118. package/src/virtual/utils/areArraysEqual.ts +13 -0
  119. package/src/virtual/utils/assert.ts +10 -0
  120. package/src/virtual/utils/getRTLOffsetType.ts +51 -0
  121. package/src/virtual/utils/getScrollbarSize.ts +24 -0
  122. package/src/virtual/utils/isRtl.ts +13 -0
  123. package/src/virtual/utils/parseNumericStyleValue.ts +19 -0
  124. package/src/virtual/utils/shallowCompare.ts +29 -0
  125. package/volar.d.ts +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indielayer/ui",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Indielayer UI Components with Tailwind CSS build for Vue 3",
5
5
  "author": {
6
6
  "name": "João Teixeira",
@@ -26,7 +26,7 @@ const selectProps = {
26
26
  },
27
27
  virtualListOverscan: {
28
28
  type: Number,
29
- default: 5,
29
+ default: 10,
30
30
  },
31
31
  placement: String as PropType<PopoverPlacement>,
32
32
  }
@@ -78,7 +78,7 @@ import XInput from '../input/Input.vue'
78
78
 
79
79
  const props = defineProps(selectProps)
80
80
 
81
- const emit = defineEmits([...useInputtable.emits(), 'close'])
81
+ const emit = defineEmits([...useInputtable.emits(), 'close', 'open'])
82
82
 
83
83
  const internalMultiple = computed(() => props.multiple || props.multipleCheckbox)
84
84
 
@@ -189,6 +189,7 @@ watch(isOpen, (isOpenValue) => {
189
189
  })
190
190
  }, 50)
191
191
 
192
+ emit('open')
192
193
  } else {
193
194
  if (props.filterable) filter.value = ''
194
195
  emit('close')
@@ -41,7 +41,7 @@ const tableProps = {
41
41
  },
42
42
  virtualListOverscan: {
43
43
  type: Number,
44
- default: 5,
44
+ default: 10,
45
45
  },
46
46
  keyProp: String,
47
47
  selectable: Boolean,
@@ -21,7 +21,7 @@ export type UseVirtualListOptions = {
21
21
  /**
22
22
  * the extra buffer items outside of the view area
23
23
  *
24
- * @default 5
24
+ * @default 10
25
25
  */
26
26
  overscan?: number;
27
27
  }
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export { default as version } from './version'
3
3
  export * from './components'
4
4
  export * from './composables'
5
5
  export * from './themes'
6
+ export * from './virtual'
6
7
  export type { UITheme, ComponentThemes } from './theme'
7
8
 
8
9
  export { default as createUI, type UIOptions } from './create'
package/src/install.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import * as components from './components'
2
+ import { XVirtualGrid, XVirtualList, XInfiniteLoader } from './virtual'
2
3
  import create from './create'
3
4
 
4
5
  export default create({
5
- components: Object.keys(components).map(
6
- (key) => components[key as keyof object],
7
- ),
6
+ components: [
7
+ ...Object.keys(components).map(
8
+ (key) => components[key as keyof object],
9
+ ),
10
+ XVirtualList,
11
+ XVirtualGrid,
12
+ XInfiniteLoader,
13
+ ],
8
14
  })
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '1.16.0'
1
+ export default '1.17.0'
@@ -0,0 +1,285 @@
1
+ # Indielayer - Virtual Vue
2
+
3
+ Vue 3 port of [react-window](https://github.com/bvaughn/react-window) - Efficient virtualized list and grid components using the Composition API.
4
+
5
+ ## Overview
6
+
7
+ High-performance virtualized list and grid components for Vue 3 applications. It renders only the visible items in large datasets, dramatically improving performance when dealing with thousands of rows or cells.
8
+
9
+ ## Quick Start
10
+
11
+ ### List Example
12
+
13
+ ```vue
14
+ <script setup lang="ts">
15
+ import { XVirtualList } from '@indielayer/ui'
16
+
17
+ const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
18
+ </script>
19
+
20
+ <template>
21
+ <XVirtualList
22
+ :row-count="items.length"
23
+ :row-height="50"
24
+ :style="{ height: '400px' }"
25
+ >
26
+ <template #row="{ index, style }">
27
+ <div :style="style" class="row">
28
+ {{ items[index] }}
29
+ </div>
30
+ </template>
31
+ </XVirtualList>
32
+ </template>
33
+
34
+ <style scoped>
35
+ .row {
36
+ display: flex;
37
+ align-items: center;
38
+ padding: 0 1rem;
39
+ border-bottom: 1px solid #eee;
40
+ }
41
+ </style>
42
+ ```
43
+
44
+ ### Grid Example
45
+
46
+ ```vue
47
+ <script setup lang="ts">
48
+ import { XVirtualGrid } from '@indielayer/ui'
49
+ </script>
50
+
51
+ <template>
52
+ <XVirtualGrid
53
+ :row-count="1000"
54
+ :column-count="1000"
55
+ :row-height="50"
56
+ :column-width="100"
57
+ :style="{ height: '500px', width: '800px' }"
58
+ >
59
+ <template #cell="{ rowIndex, columnIndex, style }">
60
+ <div :style="style" class="cell">
61
+ Row {{ rowIndex }}, Col {{ columnIndex }}
62
+ </div>
63
+ </template>
64
+ </XVirtualGrid>
65
+ </template>
66
+
67
+ <style scoped>
68
+ .cell {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ border: 1px solid #ddd;
73
+ }
74
+ </style>
75
+ ```
76
+
77
+ ## Features
78
+
79
+ - ⚡️ **Virtual scrolling** - Only renders visible items
80
+ - 📏 **Variable sizes** - Fixed, variable, or dynamic item sizes
81
+ - 📐 **Dynamic measurement** - Automatic height measurement with `useDynamicRowHeight`
82
+ - 🌐 **RTL support** - Right-to-left language support
83
+ - 🖥️ **SSR compatible** - Works with server-side rendering
84
+ - 📝 **TypeScript** - Full type definitions included
85
+ - 🎯 **Imperative API** - Programmatic scrolling via template refs
86
+ - ♿️ **ARIA support** - Accessibility attributes built-in
87
+ - 🔄 **Overscan** - Render extra items to reduce flicker
88
+ - ♾️ **Infinite loading** - Built-in support for infinite scroll/pagination
89
+
90
+ ## Components & Composables
91
+
92
+ ### List
93
+
94
+ Virtualized list component for rendering large datasets with many rows.
95
+
96
+ ```vue
97
+ <script setup lang="ts">
98
+ import { XVirtualList } from '@indielayer/ui'
99
+
100
+ const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
101
+ </script>
102
+
103
+ <template>
104
+ <XVirtualList
105
+ :row-count="items.length"
106
+ :row-height="50"
107
+ :style="{ height: '400px' }"
108
+ >
109
+ <template #row="{ index, style }">
110
+ <div :style="style">
111
+ {{ items[index] }}
112
+ </div>
113
+ </template>
114
+ </XVirtualList>
115
+ </template>
116
+ ```
117
+
118
+ ### Grid
119
+
120
+ Virtualized grid component for rendering data with rows and columns.
121
+
122
+ ```vue
123
+ <script setup lang="ts">
124
+ import { XVirtualGrid } from '@indielayer/ui'
125
+
126
+ const rowCount = 1000
127
+ const columnCount = 1000
128
+ </script>
129
+
130
+ <template>
131
+ <XVirtualGrid
132
+ :row-count="rowCount"
133
+ :column-count="columnCount"
134
+ :row-height="50"
135
+ :column-width="100"
136
+ :style="{ height: '400px', width: '600px' }"
137
+ >
138
+ <template #cell="{ rowIndex, columnIndex, style }">
139
+ <div :style="style">
140
+ Row {{ rowIndex }}, Col {{ columnIndex }}
141
+ </div>
142
+ </template>
143
+ </XVirtualGrid>
144
+ </template>
145
+ ```
146
+
147
+ ### Infinite Loader
148
+
149
+ Utility for loading data on-demand as users scroll through lists.
150
+
151
+ **Using the composable (recommended):**
152
+
153
+ ```vue
154
+ <script setup lang="ts">
155
+ import { ref } from 'vue'
156
+ import { XVirtualList, useInfiniteLoader } from '@indielayer/ui'
157
+
158
+ const items = ref<string[]>([])
159
+ const hasMoreData = ref(true)
160
+
161
+ const isRowLoaded = (index: number) => index < items.value.length
162
+
163
+ const loadMoreRows = async (startIndex: number, stopIndex: number) => {
164
+ // Simulate API call
165
+ await new Promise(resolve => setTimeout(resolve, 1000))
166
+
167
+ for (let i = startIndex; i <= stopIndex; i++) {
168
+ items.value[i] = `Item ${i}`
169
+ }
170
+
171
+ if (items.value.length >= 1000) {
172
+ hasMoreData.value = false
173
+ }
174
+ }
175
+
176
+ const { onRowsRendered } = useInfiniteLoader({
177
+ isRowLoaded,
178
+ loadMoreRows,
179
+ rowCount: hasMoreData.value ? items.value.length + 50 : items.value.length,
180
+ minimumBatchSize: 20,
181
+ threshold: 15,
182
+ })
183
+ </script>
184
+
185
+ <template>
186
+ <XVirtualList
187
+ :row-count="hasMoreData ? items.length + 50 : items.length"
188
+ :row-height="50"
189
+ :style="{ height: '400px' }"
190
+ :on-rows-rendered="onRowsRendered"
191
+ >
192
+ <template #row="{ index, style }">
193
+ <div :style="style">
194
+ {{ items[index] || 'Loading...' }}
195
+ </div>
196
+ </template>
197
+ </XVirtualList>
198
+ </template>
199
+ ```
200
+
201
+ **Using the component:**
202
+
203
+ ```vue
204
+ <script setup lang="ts">
205
+ import { ref } from 'vue'
206
+ import { XVirtualList, XInfiniteLoader } from '@indielayer/ui'
207
+
208
+ const items = ref<string[]>([])
209
+
210
+ const isRowLoaded = (index: number) => index < items.value.length
211
+
212
+ const loadMoreRows = async (startIndex: number, stopIndex: number) => {
213
+ for (let i = startIndex; i <= stopIndex; i++) {
214
+ items.value[i] = `Item ${i}`
215
+ }
216
+ }
217
+ </script>
218
+
219
+ <template>
220
+ <XInfiniteLoader
221
+ :is-row-loaded="isRowLoaded"
222
+ :load-more-rows="loadMoreRows"
223
+ :row-count="1000"
224
+ >
225
+ <template #default="{ onRowsRendered }">
226
+ <XVirtualList
227
+ :row-count="1000"
228
+ :row-height="50"
229
+ :style="{ height: '400px' }"
230
+ :on-rows-rendered="onRowsRendered"
231
+ >
232
+ <template #row="{ index, style }">
233
+ <div :style="style">
234
+ {{ items[index] || 'Loading...' }}
235
+ </div>
236
+ </template>
237
+ </XVirtualList>
238
+ </template>
239
+ </XInfiniteLoader>
240
+ </template>
241
+ ```
242
+
243
+ ## API
244
+
245
+ ### XVirtualList Props
246
+
247
+ | Prop | Type | Required | Description |
248
+ |------|------|----------|-------------|
249
+ | `rowCount` | `number` | ✅ | Number of rows in the list |
250
+ | `rowHeight` | `number \| string \| function` | ✅ | Row height (px, %, or function) |
251
+ | `style` | `CSSProperties` | ❌ | Container styles (should include height) |
252
+ | `rowProps` | `object` | ❌ | Additional props passed to row slot |
253
+ | `overscanCount` | `number` | ❌ | Extra rows to render (default: 3) |
254
+ | `defaultHeight` | `number` | ❌ | Default height for SSR |
255
+ | `onRowsRendered` | `function` | ❌ | Callback when visible rows change |
256
+ | `onResize` | `function` | ❌ | Callback when container resizes |
257
+
258
+ ### XVirtualGrid Props
259
+
260
+ | Prop | Type | Required | Description |
261
+ |------|------|----------|-------------|
262
+ | `rowCount` | `number` | ✅ | Number of rows |
263
+ | `columnCount` | `number` | ✅ | Number of columns |
264
+ | `rowHeight` | `number \| string \| function` | ✅ | Row height |
265
+ | `columnWidth` | `number \| string \| function` | ✅ | Column width |
266
+ | `style` | `CSSProperties` | ❌ | Container styles |
267
+ | `cellProps` | `object` | ❌ | Additional props passed to cell slot |
268
+ | `overscanCount` | `number` | ❌ | Extra rows/columns (default: 3) |
269
+ | `dir` | `'ltr' \| 'rtl' \| 'auto'` | ❌ | Text direction |
270
+ | `onCellsRendered` | `function` | ❌ | Callback when visible cells change |
271
+ | `onResize` | `function` | ❌ | Callback when container resizes |
272
+
273
+ ### Infinite Loader Props
274
+
275
+ | Prop | Type | Required | Default | Description |
276
+ |------|------|----------|---------|-------------|
277
+ | `isRowLoaded` | `(index: number) => boolean` | ✅ | - | Function to check if a row is loaded |
278
+ | `loadMoreRows` | `(start: number, stop: number) => Promise<void>` | ✅ | - | Async function to load more rows |
279
+ | `rowCount` | `number` | ✅ | - | Total row count (can be estimated) |
280
+ | `minimumBatchSize` | `number` | ❌ | 10 | Min rows to load per request |
281
+ | `threshold` | `number` | ❌ | 15 | Pre-fetch distance in rows |
282
+
283
+ ## Credits
284
+
285
+ This library is a Vue 3 port of [react-window](https://github.com/bvaughn/react-window) by Brian Vaughn. All credit for the original design and virtualization algorithms goes to the original author and contributors.
@@ -0,0 +1,96 @@
1
+ import { describe, expect, test, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { h } from 'vue'
4
+ import InfiniteLoader from './InfiniteLoader.vue'
5
+ import type { InfiniteLoaderProps } from '../../composables/infinite-loader/types'
6
+
7
+ interface InfiniteLoaderSlotProps {
8
+ onRowsRendered: (props: { startIndex: number; stopIndex: number; }) => void;
9
+ }
10
+
11
+ describe('InfiniteLoader', () => {
12
+ test('should render slot with onRowsRendered callback', () => {
13
+ const isRowLoaded = vi.fn(() => true)
14
+ const loadMoreRows = vi.fn(() => Promise.resolve())
15
+
16
+ const wrapper = mount(InfiniteLoader, {
17
+ props: {
18
+ isRowLoaded,
19
+ loadMoreRows,
20
+ rowCount: 10,
21
+ } as InfiniteLoaderProps,
22
+ slots: {
23
+ default: (slotProps: InfiniteLoaderSlotProps) => {
24
+ return h('div', { 'data-testid': 'slot-content' }, [
25
+ `Callback type: ${typeof slotProps.onRowsRendered}`,
26
+ ])
27
+ },
28
+ },
29
+ })
30
+
31
+ expect(wrapper.html()).toContain('Callback type: function')
32
+ })
33
+
34
+ test('should pass through onRowsRendered callback that triggers loadMoreRows', () => {
35
+ const loadMoreRows = vi.fn(() => Promise.resolve())
36
+
37
+ let capturedCallback: ((props: { startIndex: number; stopIndex: number; }) => void) | null = null
38
+
39
+ mount(InfiniteLoader, {
40
+ props: {
41
+ isRowLoaded: (index) => index <= 2,
42
+ loadMoreRows,
43
+ rowCount: 10,
44
+ threshold: 0, // Disable threshold for this test
45
+ minimumBatchSize: 0, // Disable minimum batch size for this test
46
+ } as InfiniteLoaderProps,
47
+ slots: {
48
+ default: (slotProps: InfiniteLoaderSlotProps) => {
49
+ capturedCallback = slotProps.onRowsRendered
50
+
51
+ return h('div')
52
+ },
53
+ },
54
+ })
55
+
56
+ expect(capturedCallback).toBeDefined()
57
+ expect(typeof capturedCallback).toBe('function')
58
+
59
+ // Call the callback if it is defined
60
+ if (capturedCallback) {
61
+ (capturedCallback as (props: { startIndex: number; stopIndex: number; }) => void)({ startIndex: 0, stopIndex: 5 })
62
+ } else {
63
+ throw new Error('capturedCallback is null')
64
+ }
65
+
66
+ // Should have triggered loadMoreRows for unloaded rows 3-5
67
+ expect(loadMoreRows).toHaveBeenCalled()
68
+ expect(loadMoreRows).toHaveBeenCalledWith(3, 5)
69
+ })
70
+
71
+ test('should work with VList integration pattern', () => {
72
+ const loadMoreRows = vi.fn(() => Promise.resolve())
73
+ const loadedRows = new Set([0, 1, 2])
74
+
75
+ const wrapper = mount(InfiniteLoader, {
76
+ props: {
77
+ isRowLoaded: (index) => loadedRows.has(index),
78
+ loadMoreRows,
79
+ rowCount: 10,
80
+ minimumBatchSize: 5,
81
+ } as InfiniteLoaderProps,
82
+ slots: {
83
+ default: (slotProps: InfiniteLoaderSlotProps) => {
84
+ // Simulate VList calling onRowsRendered
85
+ slotProps.onRowsRendered({ startIndex: 0, stopIndex: 7 })
86
+
87
+ return h('div', 'List content')
88
+ },
89
+ },
90
+ })
91
+
92
+ // Should load rows 3-7 with minimum batch size of 5
93
+ expect(loadMoreRows).toHaveBeenCalled()
94
+ expect(wrapper.html()).toContain('List content')
95
+ })
96
+ })
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'XInfiniteLoader',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { useInfiniteLoader } from '../../composables/infinite-loader/useInfiniteLoader'
9
+ import type { InfiniteLoaderProps } from '../../composables/infinite-loader/types'
10
+
11
+ const props = defineProps<InfiniteLoaderProps>()
12
+
13
+ const { onRowsRendered } = useInfiniteLoader(props)
14
+ </script>
15
+
16
+ <template>
17
+ <slot :on-rows-rendered="onRowsRendered" ></slot>
18
+ </template>