@indielayer/ui 1.16.0 → 1.18.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 (156) hide show
  1. package/README.md +2 -2
  2. package/docs/assets/css/tailwind.css +6 -0
  3. package/docs/components/common/CodePreview.vue +14 -9
  4. package/docs/components/common/DocsFeatures.vue +41 -0
  5. package/docs/components/common/DocsHero.vue +216 -0
  6. package/docs/components/common/DocumentPage.vue +99 -112
  7. package/docs/components/common/ExampleBlocks.vue +157 -0
  8. package/docs/components/menu/DocsMenu.vue +3 -0
  9. package/docs/components/toolbar/Toolbar.vue +11 -2
  10. package/docs/components/toolbar/ToolbarColorToggle.vue +4 -4
  11. package/docs/components/toolbar/ToolbarSearch.vue +59 -62
  12. package/docs/composables/useDocMeta.ts +47 -0
  13. package/docs/icons.ts +28 -0
  14. package/docs/layouts/default.vue +1 -3
  15. package/docs/layouts/simple.vue +3 -1
  16. package/docs/main.ts +5 -0
  17. package/docs/pages/colors.vue +56 -47
  18. package/docs/pages/component/infiniteLoader/composable.vue +168 -0
  19. package/docs/pages/component/infiniteLoader/index.vue +36 -0
  20. package/docs/pages/component/infiniteLoader/usage.vue +161 -0
  21. package/docs/pages/component/select/size.vue +1 -1
  22. package/docs/pages/component/select/usage.vue +14 -7
  23. package/docs/pages/component/virtualGrid/index.vue +29 -0
  24. package/docs/pages/component/virtualGrid/usage.vue +20 -0
  25. package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
  26. package/docs/pages/component/virtualList/index.vue +36 -0
  27. package/docs/pages/component/virtualList/usage.vue +17 -0
  28. package/docs/pages/error.vue +5 -3
  29. package/docs/pages/icons.vue +64 -54
  30. package/docs/pages/index.vue +93 -82
  31. package/docs/pages/typography.vue +38 -28
  32. package/docs/router/index.ts +31 -3
  33. package/docs/search/components.json +1 -1
  34. package/docs/search/index.json +1 -0
  35. package/lib/components/container/theme/Container.base.theme.js +1 -1
  36. package/lib/components/divider/theme/Divider.base.theme.js +1 -1
  37. package/lib/components/input/Input.vue.js +23 -24
  38. package/lib/components/select/Select.vue.d.ts +16 -27
  39. package/lib/components/select/Select.vue.js +452 -345
  40. package/lib/components/table/Table.vue.js +1 -1
  41. package/lib/composables/useVirtualList.d.ts +1 -1
  42. package/lib/index.d.ts +1 -0
  43. package/lib/index.js +88 -76
  44. package/lib/index.umd.js +4 -4
  45. package/lib/install.js +15 -7
  46. package/lib/version.d.ts +1 -1
  47. package/lib/version.js +1 -1
  48. package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
  49. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
  50. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
  51. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
  52. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
  53. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
  54. package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
  55. package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
  56. package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
  57. package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
  58. package/lib/virtual/components/virtualList/VirtualList.vue.js +159 -0
  59. package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
  60. package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
  61. package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
  62. package/lib/virtual/components/virtualList/types.d.ts +115 -0
  63. package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
  64. package/lib/virtual/components/virtualList/useDynamicRowHeight.js +68 -0
  65. package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
  66. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
  67. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
  68. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
  69. package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
  70. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
  71. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
  72. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
  73. package/lib/virtual/core/createCachedBounds.d.ts +6 -0
  74. package/lib/virtual/core/createCachedBounds.js +55 -0
  75. package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
  76. package/lib/virtual/core/getEstimatedSize.js +22 -0
  77. package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
  78. package/lib/virtual/core/getOffsetForIndex.js +40 -0
  79. package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
  80. package/lib/virtual/core/getStartStopIndices.js +31 -0
  81. package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
  82. package/lib/virtual/core/types.d.ts +11 -0
  83. package/lib/virtual/core/useCachedBounds.d.ts +7 -0
  84. package/lib/virtual/core/useCachedBounds.js +18 -0
  85. package/lib/virtual/core/useIsRtl.d.ts +2 -0
  86. package/lib/virtual/core/useIsRtl.js +15 -0
  87. package/lib/virtual/core/useItemSize.d.ts +5 -0
  88. package/lib/virtual/core/useItemSize.js +27 -0
  89. package/lib/virtual/core/useVirtualizer.d.ts +33 -0
  90. package/lib/virtual/core/useVirtualizer.js +171 -0
  91. package/lib/virtual/index.d.ts +9 -0
  92. package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
  93. package/lib/virtual/types.d.ts +2 -0
  94. package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
  95. package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
  96. package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
  97. package/lib/virtual/utils/assert.d.ts +1 -0
  98. package/lib/virtual/utils/assert.js +7 -0
  99. package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
  100. package/lib/virtual/utils/getRTLOffsetType.js +13 -0
  101. package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
  102. package/lib/virtual/utils/getScrollbarSize.js +11 -0
  103. package/lib/virtual/utils/isRtl.d.ts +1 -0
  104. package/lib/virtual/utils/isRtl.js +12 -0
  105. package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
  106. package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
  107. package/lib/virtual/utils/shallowCompare.d.ts +1 -0
  108. package/lib/virtual/utils/shallowCompare.js +14 -0
  109. package/package.json +8 -3
  110. package/src/components/container/theme/Container.base.theme.ts +1 -1
  111. package/src/components/divider/theme/Divider.base.theme.ts +1 -1
  112. package/src/components/input/Input.vue +1 -2
  113. package/src/components/select/Select.vue +97 -20
  114. package/src/components/table/Table.vue +1 -1
  115. package/src/composables/useVirtualList.ts +1 -1
  116. package/src/index.ts +1 -0
  117. package/src/install.ts +9 -3
  118. package/src/version.ts +1 -1
  119. package/src/virtual/README.md +285 -0
  120. package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
  121. package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
  122. package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
  123. package/src/virtual/components/virtualGrid/types.ts +160 -0
  124. package/src/virtual/components/virtualList/VirtualList.test.ts +164 -0
  125. package/src/virtual/components/virtualList/VirtualList.vue +227 -0
  126. package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
  127. package/src/virtual/components/virtualList/types.ts +127 -0
  128. package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +197 -0
  129. package/src/virtual/components/virtualList/useDynamicRowHeight.ts +149 -0
  130. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
  131. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
  132. package/src/virtual/composables/infinite-loader/types.ts +36 -0
  133. package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
  134. package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
  135. package/src/virtual/core/createCachedBounds.ts +72 -0
  136. package/src/virtual/core/getEstimatedSize.ts +29 -0
  137. package/src/virtual/core/getOffsetForIndex.ts +90 -0
  138. package/src/virtual/core/getStartStopIndices.test.ts +45 -0
  139. package/src/virtual/core/getStartStopIndices.ts +71 -0
  140. package/src/virtual/core/types.ts +17 -0
  141. package/src/virtual/core/useCachedBounds.ts +21 -0
  142. package/src/virtual/core/useIsRtl.ts +25 -0
  143. package/src/virtual/core/useItemSize.ts +34 -0
  144. package/src/virtual/core/useVirtualizer.ts +294 -0
  145. package/src/virtual/index.ts +25 -0
  146. package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
  147. package/src/virtual/types.ts +3 -0
  148. package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
  149. package/src/virtual/utils/areArraysEqual.ts +13 -0
  150. package/src/virtual/utils/assert.ts +10 -0
  151. package/src/virtual/utils/getRTLOffsetType.ts +51 -0
  152. package/src/virtual/utils/getScrollbarSize.ts +24 -0
  153. package/src/virtual/utils/isRtl.ts +13 -0
  154. package/src/virtual/utils/parseNumericStyleValue.ts +21 -0
  155. package/src/virtual/utils/shallowCompare.ts +29 -0
  156. package/volar.d.ts +3 -0
@@ -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>
@@ -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
+ }