@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
@@ -0,0 +1,322 @@
1
+ <script lang="ts">
2
+ export default {
3
+ name: 'XVirtualGrid',
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, ref, watch, type CSSProperties } from 'vue'
9
+ import { useIsRtl } from '../../core/useIsRtl'
10
+ import { useVirtualizer } from '../../core/useVirtualizer'
11
+ import type { Align } from '../../types'
12
+ import type { VirtualGridProps, VirtualGridImperativeAPI } from './types'
13
+
14
+ const props = withDefaults(defineProps<VirtualGridProps>(), {
15
+ defaultHeight: 0,
16
+ defaultWidth: 0,
17
+ overscanCount: 3,
18
+ tag: 'div',
19
+ })
20
+
21
+ const element = ref<HTMLDivElement | null>(null)
22
+
23
+ const cellProps = computed(() => props.cellProps || ({} as Record<string, unknown>))
24
+
25
+ const isRtl = useIsRtl(element, props.dir)
26
+
27
+ const {
28
+ getCellBounds: getColumnBounds,
29
+ getEstimatedSize: getEstimatedWidth,
30
+ startIndexOverscan: columnStartIndexOverscan,
31
+ startIndexVisible: columnStartIndexVisible,
32
+ scrollToIndex: scrollToColumnIndex,
33
+ stopIndexOverscan: columnStopIndexOverscan,
34
+ stopIndexVisible: columnStopIndexVisible,
35
+ } = useVirtualizer({
36
+ containerElement: element,
37
+ containerStyle: props.style,
38
+ defaultContainerSize: props.defaultWidth,
39
+ direction: 'horizontal',
40
+ isRtl: isRtl.value,
41
+ itemCount: props.columnCount,
42
+ itemProps: cellProps.value,
43
+ itemSize: props.columnWidth,
44
+ onResize: props.onResize,
45
+ overscanCount: props.overscanCount,
46
+ })
47
+
48
+ const {
49
+ getCellBounds: getRowBounds,
50
+ getEstimatedSize: getEstimatedHeight,
51
+ startIndexOverscan: rowStartIndexOverscan,
52
+ startIndexVisible: rowStartIndexVisible,
53
+ scrollToIndex: scrollToRowIndex,
54
+ stopIndexOverscan: rowStopIndexOverscan,
55
+ stopIndexVisible: rowStopIndexVisible,
56
+ } = useVirtualizer({
57
+ containerElement: element,
58
+ containerStyle: props.style,
59
+ defaultContainerSize: props.defaultHeight,
60
+ direction: 'vertical',
61
+ itemCount: props.rowCount,
62
+ itemProps: cellProps.value,
63
+ itemSize: props.rowHeight,
64
+ onResize: props.onResize,
65
+ overscanCount: props.overscanCount,
66
+ })
67
+
68
+ // Expose imperative API
69
+ defineExpose<VirtualGridImperativeAPI>({
70
+ get element() {
71
+ return element.value
72
+ },
73
+
74
+ scrollToCell({
75
+ behavior = 'auto',
76
+ columnAlign = 'auto',
77
+ columnIndex,
78
+ rowAlign = 'auto',
79
+ rowIndex,
80
+ }: {
81
+ behavior?: ScrollBehavior;
82
+ columnAlign?: Align;
83
+ columnIndex: number;
84
+ rowAlign?: Align;
85
+ rowIndex: number;
86
+ }) {
87
+ const left = scrollToColumnIndex({
88
+ align: columnAlign,
89
+ containerScrollOffset: element.value?.scrollLeft ?? 0,
90
+ index: columnIndex,
91
+ })
92
+ const top = scrollToRowIndex({
93
+ align: rowAlign,
94
+ containerScrollOffset: element.value?.scrollTop ?? 0,
95
+ index: rowIndex,
96
+ })
97
+
98
+ if (typeof element.value?.scrollTo === 'function' && left !== undefined && top !== undefined) {
99
+ element.value.scrollTo({
100
+ behavior,
101
+ left,
102
+ top,
103
+ })
104
+ }
105
+ },
106
+
107
+ scrollToColumn({
108
+ align = 'auto',
109
+ behavior = 'auto',
110
+ index,
111
+ }: {
112
+ align?: Align;
113
+ behavior?: ScrollBehavior;
114
+ index: number;
115
+ }) {
116
+ const left = scrollToColumnIndex({
117
+ align,
118
+ containerScrollOffset: element.value?.scrollLeft ?? 0,
119
+ index,
120
+ })
121
+
122
+ if (typeof element.value?.scrollTo === 'function' && left !== undefined) {
123
+ element.value.scrollTo({
124
+ behavior,
125
+ left,
126
+ })
127
+ }
128
+ },
129
+
130
+ scrollToRow({
131
+ align = 'auto',
132
+ behavior = 'auto',
133
+ index,
134
+ }: {
135
+ align?: Align;
136
+ behavior?: ScrollBehavior;
137
+ index: number;
138
+ }) {
139
+ const top = scrollToRowIndex({
140
+ align,
141
+ containerScrollOffset: element.value?.scrollTop ?? 0,
142
+ index,
143
+ })
144
+
145
+ if (typeof element.value?.scrollTo === 'function' && top !== undefined) {
146
+ element.value.scrollTo({
147
+ behavior,
148
+ top,
149
+ })
150
+ }
151
+ },
152
+ })
153
+
154
+ // Notify when visible cells change
155
+ watch(
156
+ [
157
+ columnStartIndexOverscan,
158
+ columnStartIndexVisible,
159
+ columnStopIndexOverscan,
160
+ columnStopIndexVisible,
161
+ rowStartIndexOverscan,
162
+ rowStartIndexVisible,
163
+ rowStopIndexOverscan,
164
+ rowStopIndexVisible,
165
+ ],
166
+ ([
167
+ colStartOverscan,
168
+ colStartVisible,
169
+ colStopOverscan,
170
+ colStopVisible,
171
+ rowStartOverscan,
172
+ rowStartVisible,
173
+ rowStopOverscan,
174
+ rowStopVisible,
175
+ ]) => {
176
+ if (
177
+ colStartOverscan >= 0 &&
178
+ colStopOverscan >= 0 &&
179
+ rowStartOverscan >= 0 &&
180
+ rowStopOverscan >= 0 &&
181
+ props.onCellsRendered
182
+ ) {
183
+ props.onCellsRendered(
184
+ {
185
+ columnStartIndex: colStartVisible,
186
+ columnStopIndex: colStopVisible,
187
+ rowStartIndex: rowStartVisible,
188
+ rowStopIndex: rowStopVisible,
189
+ },
190
+ {
191
+ columnStartIndex: colStartOverscan,
192
+ columnStopIndex: colStopOverscan,
193
+ rowStartIndex: rowStartOverscan,
194
+ rowStopIndex: rowStopOverscan,
195
+ },
196
+ )
197
+ }
198
+ },
199
+ )
200
+
201
+ interface CellData {
202
+ key: number;
203
+ columnIndex: number;
204
+ rowIndex: number;
205
+ style: CSSProperties;
206
+ ariaAttributes: {
207
+ 'aria-colindex': number;
208
+ role: 'gridcell';
209
+ };
210
+ }
211
+
212
+ interface RowData {
213
+ key: number;
214
+ rowIndex: number;
215
+ columns: CellData[];
216
+ ariaRowIndex: number;
217
+ }
218
+
219
+ // Generate cells
220
+ const rows = computed(() => {
221
+ const result: RowData[] = []
222
+
223
+ if (props.columnCount > 0 && props.rowCount > 0) {
224
+ for (
225
+ let rowIndex = rowStartIndexOverscan.value;
226
+ rowIndex <= rowStopIndexOverscan.value;
227
+ rowIndex++
228
+ ) {
229
+ const rowBounds = getRowBounds(rowIndex)
230
+
231
+ const columns: CellData[] = []
232
+
233
+ for (
234
+ let columnIndex = columnStartIndexOverscan.value;
235
+ columnIndex <= columnStopIndexOverscan.value;
236
+ columnIndex++
237
+ ) {
238
+ const columnBounds = getColumnBounds(columnIndex)
239
+
240
+ const cellStyle: CSSProperties = {
241
+ position: 'absolute',
242
+ left: isRtl.value ? undefined : 0,
243
+ right: isRtl.value ? 0 : undefined,
244
+ transform: `translate(${isRtl.value ? -columnBounds.scrollOffset : columnBounds.scrollOffset}px, ${rowBounds.scrollOffset}px)`,
245
+ height: `${rowBounds.size}px`,
246
+ width: `${columnBounds.size}px`,
247
+ }
248
+
249
+ columns.push({
250
+ key: columnIndex,
251
+ columnIndex,
252
+ rowIndex,
253
+ style: cellStyle,
254
+ ariaAttributes: {
255
+ 'aria-colindex': columnIndex + 1,
256
+ role: 'gridcell',
257
+ },
258
+ })
259
+ }
260
+
261
+ result.push({
262
+ key: rowIndex,
263
+ rowIndex,
264
+ columns,
265
+ ariaRowIndex: rowIndex + 1,
266
+ })
267
+ }
268
+ }
269
+
270
+ return result
271
+ })
272
+ </script>
273
+
274
+ <template>
275
+ <component
276
+ :is="tag"
277
+ ref="element"
278
+ :class="$props.class"
279
+ :style="{
280
+ position: 'relative',
281
+ maxHeight: '100%',
282
+ maxWidth: '100%',
283
+ flexGrow: 1,
284
+ overflow: 'auto',
285
+ ...style
286
+ }"
287
+ :dir="dir"
288
+ role="grid"
289
+ :aria-colcount="columnCount"
290
+ :aria-rowcount="rowCount"
291
+ >
292
+ <div
293
+ v-for="row in rows"
294
+ :key="row.key"
295
+ role="row"
296
+ :aria-rowindex="row.ariaRowIndex"
297
+ >
298
+ <template v-for="cell in row.columns" :key="cell.key">
299
+ <slot
300
+ name="cell"
301
+ :column-index="cell.columnIndex"
302
+ :row-index="cell.rowIndex"
303
+ :style="cell.style"
304
+ :aria-attributes="cell.ariaAttributes"
305
+ :props="cellProps"
306
+ ></slot>
307
+ </template>
308
+ </div>
309
+
310
+ <slot ></slot>
311
+
312
+ <!-- Sizing element -->
313
+ <div
314
+ aria-hidden
315
+ :style="{
316
+ height: `${getEstimatedHeight}px`,
317
+ width: `${getEstimatedWidth}px`,
318
+ zIndex: -1
319
+ }"
320
+ ></div>
321
+ </component>
322
+ </template>
@@ -0,0 +1,160 @@
1
+ import type { CSSProperties } from 'vue'
2
+ import type { TagNames } from '../../types'
3
+
4
+ type ForbiddenKeys = 'ariaAttributes' | 'columnIndex' | 'rowIndex' | 'style';
5
+ type ExcludeForbiddenKeys<Type> = {
6
+ [Key in keyof Type]: Key extends ForbiddenKeys ? never : Type[Key];
7
+ };
8
+
9
+ export interface VirtualGridProps {
10
+ /**
11
+ * Additional props to be passed to the cell-rendering component via slots.
12
+ */
13
+ cellProps?: ExcludeForbiddenKeys<Record<string, unknown>>;
14
+
15
+ /**
16
+ * CSS class name.
17
+ */
18
+ class?: string;
19
+
20
+ /**
21
+ * Number of columns to be rendered in the grid.
22
+ */
23
+ columnCount: number;
24
+
25
+ /**
26
+ * Column width; the following formats are supported:
27
+ * - number of pixels (number)
28
+ * - percentage of the grid's current width (string)
29
+ * - function that returns the column width (in pixels) given an index and `cellProps`
30
+ */
31
+ columnWidth:
32
+ | number
33
+ | string
34
+ | ((index: number, cellProps: Record<string, unknown>) => number);
35
+
36
+ /**
37
+ * Default height of grid for initial render.
38
+ * This value is important for server rendering.
39
+ */
40
+ defaultHeight?: number;
41
+
42
+ /**
43
+ * Default width of grid for initial render.
44
+ * This value is important for server rendering.
45
+ */
46
+ defaultWidth?: number;
47
+
48
+ /**
49
+ * Indicates the directionality of grid cells.
50
+ */
51
+ dir?: 'ltr' | 'rtl' | 'auto';
52
+
53
+ /**
54
+ * Callback notified when the range of rendered cells changes.
55
+ */
56
+ onCellsRendered?: (
57
+ visibleCells: {
58
+ columnStartIndex: number;
59
+ columnStopIndex: number;
60
+ rowStartIndex: number;
61
+ rowStopIndex: number;
62
+ },
63
+ allCells: {
64
+ columnStartIndex: number;
65
+ columnStopIndex: number;
66
+ rowStartIndex: number;
67
+ rowStopIndex: number;
68
+ }
69
+ ) => void;
70
+
71
+ /**
72
+ * Callback notified when the Grid's outermost HTMLElement resizes.
73
+ * This may be used to (re)scroll a cell into view.
74
+ */
75
+ onResize?: (
76
+ size: { height: number; width: number; },
77
+ prevSize: { height: number; width: number; }
78
+ ) => void;
79
+
80
+ /**
81
+ * How many additional rows/columns to render outside of the visible area.
82
+ * This can reduce visual flickering near the edges of a grid when scrolling.
83
+ */
84
+ overscanCount?: number;
85
+
86
+ /**
87
+ * Number of rows to be rendered in the grid.
88
+ */
89
+ rowCount: number;
90
+
91
+ /**
92
+ * Row height; the following formats are supported:
93
+ * - number of pixels (number)
94
+ * - percentage of the grid's current height (string)
95
+ * - function that returns the row height (in pixels) given an index and `cellProps`
96
+ */
97
+ rowHeight: number | string | ((index: number, cellProps: Record<string, unknown>) => number);
98
+
99
+ /**
100
+ * Optional CSS properties.
101
+ * The grid of cells will fill the height and width defined by this style.
102
+ */
103
+ style?: CSSProperties;
104
+
105
+ /**
106
+ * Can be used to override the root HTML element rendered by the Grid component.
107
+ * The default value is "div", meaning that Grid renders an HTMLDivElement as its root.
108
+ */
109
+ tag?: TagNames;
110
+ }
111
+
112
+ export interface CellSlotProps {
113
+ ariaAttributes: {
114
+ 'aria-colindex': number;
115
+ role: 'gridcell';
116
+ };
117
+ columnIndex: number;
118
+ rowIndex: number;
119
+ style: CSSProperties;
120
+ props?: Record<string, unknown>;
121
+ }
122
+
123
+ /**
124
+ * Imperative Grid API.
125
+ */
126
+ export interface VirtualGridImperativeAPI {
127
+ /**
128
+ * Outermost HTML element for the grid if mounted and null (if not mounted.
129
+ */
130
+ readonly element: HTMLDivElement | null;
131
+
132
+ /**
133
+ * Scrolls the grid so that the specified cell is visible.
134
+ */
135
+ scrollToCell(config: {
136
+ behavior?: ScrollBehavior;
137
+ columnAlign?: 'auto' | 'center' | 'end' | 'smart' | 'start';
138
+ columnIndex: number;
139
+ rowAlign?: 'auto' | 'center' | 'end' | 'smart' | 'start';
140
+ rowIndex: number;
141
+ }): void;
142
+
143
+ /**
144
+ * Scrolls the grid so that the specified column is visible.
145
+ */
146
+ scrollToColumn(config: {
147
+ align?: 'auto' | 'center' | 'end' | 'smart' | 'start';
148
+ behavior?: ScrollBehavior;
149
+ index: number;
150
+ }): void;
151
+
152
+ /**
153
+ * Scrolls the grid so that the specified row is visible.
154
+ */
155
+ scrollToRow(config: {
156
+ align?: 'auto' | 'center' | 'end' | 'smart' | 'start';
157
+ behavior?: ScrollBehavior;
158
+ index: number;
159
+ }): void;
160
+ }
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { h, type CSSProperties } from 'vue'
4
+ import VirtualList from './VirtualList.vue'
5
+
6
+ interface RowSlotProps {
7
+ index: number;
8
+ style: CSSProperties;
9
+ }
10
+
11
+ describe('VirtualList', () => {
12
+ it('renders correctly', () => {
13
+ const wrapper = mount(VirtualList, {
14
+ props: {
15
+ rowCount: 100,
16
+ rowHeight: 50,
17
+ style: { height: '400px' },
18
+ },
19
+ slots: {
20
+ row: ({ index, style }: RowSlotProps) =>
21
+ h('div', { style }, `Row ${index}`),
22
+ },
23
+ })
24
+
25
+ expect(wrapper.exists()).toBe(true)
26
+ expect(wrapper.attributes('role')).toBe('list')
27
+ })
28
+
29
+ it('renders visible rows', () => {
30
+ const wrapper = mount(VirtualList, {
31
+ props: {
32
+ rowCount: 100,
33
+ rowHeight: 50,
34
+ style: { height: '400px' },
35
+ },
36
+ slots: {
37
+ row: ({ index, style }: RowSlotProps) =>
38
+ h('div', { style, class: 'test-row' }, `Row ${index}`),
39
+ },
40
+ })
41
+
42
+ // Should render some rows (visible + overscan)
43
+ const rows = wrapper.findAll('.test-row')
44
+
45
+ expect(rows.length).toBeGreaterThan(0)
46
+ })
47
+ })