@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.
- package/docs/components/menu/DocsMenu.vue +3 -0
- package/docs/pages/component/infiniteLoader/composable.vue +168 -0
- package/docs/pages/component/infiniteLoader/index.vue +36 -0
- package/docs/pages/component/infiniteLoader/usage.vue +161 -0
- package/docs/pages/component/virtualGrid/index.vue +29 -0
- package/docs/pages/component/virtualGrid/usage.vue +20 -0
- package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
- package/docs/pages/component/virtualList/index.vue +36 -0
- package/docs/pages/component/virtualList/usage.vue +17 -0
- package/docs/search/components.json +1 -1
- package/lib/components/select/Select.vue.js +35 -35
- package/lib/components/table/Table.vue.js +1 -1
- package/lib/composables/useVirtualList.d.ts +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +88 -76
- package/lib/index.umd.js +4 -4
- package/lib/install.js +15 -7
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
- package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
- package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
- package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
- package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
- package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
- package/lib/virtual/components/virtualList/VirtualList.vue.js +157 -0
- package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
- package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
- package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
- package/lib/virtual/components/virtualList/types.d.ts +115 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.js +69 -0
- package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
- package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
- package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
- package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
- package/lib/virtual/core/createCachedBounds.d.ts +6 -0
- package/lib/virtual/core/createCachedBounds.js +55 -0
- package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
- package/lib/virtual/core/getEstimatedSize.js +22 -0
- package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
- package/lib/virtual/core/getOffsetForIndex.js +40 -0
- package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
- package/lib/virtual/core/getStartStopIndices.js +31 -0
- package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
- package/lib/virtual/core/types.d.ts +11 -0
- package/lib/virtual/core/useCachedBounds.d.ts +7 -0
- package/lib/virtual/core/useCachedBounds.js +18 -0
- package/lib/virtual/core/useIsRtl.d.ts +2 -0
- package/lib/virtual/core/useIsRtl.js +15 -0
- package/lib/virtual/core/useItemSize.d.ts +5 -0
- package/lib/virtual/core/useItemSize.js +27 -0
- package/lib/virtual/core/useVirtualizer.d.ts +33 -0
- package/lib/virtual/core/useVirtualizer.js +171 -0
- package/lib/virtual/index.d.ts +9 -0
- package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
- package/lib/virtual/types.d.ts +2 -0
- package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
- package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
- package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
- package/lib/virtual/utils/assert.d.ts +1 -0
- package/lib/virtual/utils/assert.js +7 -0
- package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
- package/lib/virtual/utils/getRTLOffsetType.js +13 -0
- package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
- package/lib/virtual/utils/getScrollbarSize.js +11 -0
- package/lib/virtual/utils/isRtl.d.ts +1 -0
- package/lib/virtual/utils/isRtl.js +12 -0
- package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
- package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
- package/lib/virtual/utils/shallowCompare.d.ts +1 -0
- package/lib/virtual/utils/shallowCompare.js +14 -0
- package/package.json +1 -1
- package/src/components/select/Select.vue +3 -2
- package/src/components/table/Table.vue +1 -1
- package/src/composables/useVirtualList.ts +1 -1
- package/src/index.ts +1 -0
- package/src/install.ts +9 -3
- package/src/version.ts +1 -1
- package/src/virtual/README.md +285 -0
- package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
- package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
- package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
- package/src/virtual/components/virtualGrid/types.ts +160 -0
- package/src/virtual/components/virtualList/VirtualList.test.ts +47 -0
- package/src/virtual/components/virtualList/VirtualList.vue +233 -0
- package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
- package/src/virtual/components/virtualList/types.ts +127 -0
- package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +183 -0
- package/src/virtual/components/virtualList/useDynamicRowHeight.ts +147 -0
- package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
- package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
- package/src/virtual/composables/infinite-loader/types.ts +36 -0
- package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
- package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
- package/src/virtual/core/createCachedBounds.ts +72 -0
- package/src/virtual/core/getEstimatedSize.ts +29 -0
- package/src/virtual/core/getOffsetForIndex.ts +90 -0
- package/src/virtual/core/getStartStopIndices.test.ts +45 -0
- package/src/virtual/core/getStartStopIndices.ts +71 -0
- package/src/virtual/core/types.ts +17 -0
- package/src/virtual/core/useCachedBounds.ts +21 -0
- package/src/virtual/core/useIsRtl.ts +25 -0
- package/src/virtual/core/useItemSize.ts +34 -0
- package/src/virtual/core/useVirtualizer.ts +294 -0
- package/src/virtual/index.ts +25 -0
- package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
- package/src/virtual/types.ts +3 -0
- package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
- package/src/virtual/utils/areArraysEqual.ts +13 -0
- package/src/virtual/utils/assert.ts +10 -0
- package/src/virtual/utils/getRTLOffsetType.ts +51 -0
- package/src/virtual/utils/getScrollbarSize.ts +24 -0
- package/src/virtual/utils/isRtl.ts +13 -0
- package/src/virtual/utils/parseNumericStyleValue.ts +19 -0
- package/src/virtual/utils/shallowCompare.ts +29 -0
- package/volar.d.ts +3 -0
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ const selectProps = {
|
|
|
26
26
|
},
|
|
27
27
|
virtualListOverscan: {
|
|
28
28
|
type: Number,
|
|
29
|
-
default:
|
|
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')
|
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:
|
|
6
|
-
(
|
|
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.
|
|
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>
|