@indielayer/ui 1.15.3 → 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 (129) 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/table/usage.vue +13 -0
  6. package/docs/pages/component/virtualGrid/index.vue +29 -0
  7. package/docs/pages/component/virtualGrid/usage.vue +20 -0
  8. package/docs/pages/component/virtualList/dynamicHeight.vue +75 -0
  9. package/docs/pages/component/virtualList/index.vue +36 -0
  10. package/docs/pages/component/virtualList/usage.vue +17 -0
  11. package/docs/search/components.json +1 -1
  12. package/lib/components/select/Select.vue.js +35 -35
  13. package/lib/components/table/Table.vue.d.ts +9 -0
  14. package/lib/components/table/Table.vue.js +190 -160
  15. package/lib/components/tooltip/Tooltip.vue.js +64 -52
  16. package/lib/composables/useVirtualList.d.ts +1 -1
  17. package/lib/index.d.ts +1 -0
  18. package/lib/index.js +88 -76
  19. package/lib/index.umd.js +4 -4
  20. package/lib/install.js +15 -7
  21. package/lib/version.d.ts +1 -1
  22. package/lib/version.js +1 -1
  23. package/lib/virtual/components/infiniteLoader/InfiniteLoader.test.d.ts +1 -0
  24. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.d.ts +49 -0
  25. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue.js +21 -0
  26. package/lib/virtual/components/infiniteLoader/InfiniteLoader.vue2.js +4 -0
  27. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.d.ts +185 -0
  28. package/lib/virtual/components/virtualGrid/VirtualGrid.vue.js +241 -0
  29. package/lib/virtual/components/virtualGrid/VirtualGrid.vue2.js +4 -0
  30. package/lib/virtual/components/virtualGrid/types.d.ts +138 -0
  31. package/lib/virtual/components/virtualList/VirtualList.test.d.ts +1 -0
  32. package/lib/virtual/components/virtualList/VirtualList.vue.d.ts +135 -0
  33. package/lib/virtual/components/virtualList/VirtualList.vue.js +157 -0
  34. package/lib/virtual/components/virtualList/VirtualList.vue2.js +4 -0
  35. package/lib/virtual/components/virtualList/isDynamicRowHeight.d.ts +2 -0
  36. package/lib/virtual/components/virtualList/isDynamicRowHeight.js +6 -0
  37. package/lib/virtual/components/virtualList/types.d.ts +115 -0
  38. package/lib/virtual/components/virtualList/useDynamicRowHeight.d.ts +7 -0
  39. package/lib/virtual/components/virtualList/useDynamicRowHeight.js +69 -0
  40. package/lib/virtual/components/virtualList/useDynamicRowHeight.test.d.ts +1 -0
  41. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.d.ts +8 -0
  42. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.js +41 -0
  43. package/lib/virtual/composables/infinite-loader/scanForUnloadedIndices.test.d.ts +1 -0
  44. package/lib/virtual/composables/infinite-loader/types.d.ts +30 -0
  45. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.d.ts +6 -0
  46. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.js +42 -0
  47. package/lib/virtual/composables/infinite-loader/useInfiniteLoader.test.d.ts +1 -0
  48. package/lib/virtual/core/createCachedBounds.d.ts +6 -0
  49. package/lib/virtual/core/createCachedBounds.js +55 -0
  50. package/lib/virtual/core/getEstimatedSize.d.ts +6 -0
  51. package/lib/virtual/core/getEstimatedSize.js +22 -0
  52. package/lib/virtual/core/getOffsetForIndex.d.ts +11 -0
  53. package/lib/virtual/core/getOffsetForIndex.js +40 -0
  54. package/lib/virtual/core/getStartStopIndices.d.ts +13 -0
  55. package/lib/virtual/core/getStartStopIndices.js +31 -0
  56. package/lib/virtual/core/getStartStopIndices.test.d.ts +1 -0
  57. package/lib/virtual/core/types.d.ts +11 -0
  58. package/lib/virtual/core/useCachedBounds.d.ts +7 -0
  59. package/lib/virtual/core/useCachedBounds.js +18 -0
  60. package/lib/virtual/core/useIsRtl.d.ts +2 -0
  61. package/lib/virtual/core/useIsRtl.js +15 -0
  62. package/lib/virtual/core/useItemSize.d.ts +5 -0
  63. package/lib/virtual/core/useItemSize.js +27 -0
  64. package/lib/virtual/core/useVirtualizer.d.ts +33 -0
  65. package/lib/virtual/core/useVirtualizer.js +171 -0
  66. package/lib/virtual/index.d.ts +9 -0
  67. package/lib/virtual/test-utils/mockResizeObserver.d.ts +15 -0
  68. package/lib/virtual/types.d.ts +2 -0
  69. package/lib/virtual/utils/adjustScrollOffsetForRtl.d.ts +7 -0
  70. package/lib/virtual/utils/adjustScrollOffsetForRtl.js +24 -0
  71. package/lib/virtual/utils/areArraysEqual.d.ts +1 -0
  72. package/lib/virtual/utils/assert.d.ts +1 -0
  73. package/lib/virtual/utils/assert.js +7 -0
  74. package/lib/virtual/utils/getRTLOffsetType.d.ts +2 -0
  75. package/lib/virtual/utils/getRTLOffsetType.js +13 -0
  76. package/lib/virtual/utils/getScrollbarSize.d.ts +2 -0
  77. package/lib/virtual/utils/getScrollbarSize.js +11 -0
  78. package/lib/virtual/utils/isRtl.d.ts +1 -0
  79. package/lib/virtual/utils/isRtl.js +12 -0
  80. package/lib/virtual/utils/parseNumericStyleValue.d.ts +2 -0
  81. package/lib/virtual/utils/parseNumericStyleValue.js +15 -0
  82. package/lib/virtual/utils/shallowCompare.d.ts +1 -0
  83. package/lib/virtual/utils/shallowCompare.js +14 -0
  84. package/package.json +1 -1
  85. package/src/components/select/Select.vue +3 -2
  86. package/src/components/table/Table.vue +23 -2
  87. package/src/components/tooltip/Tooltip.vue +25 -5
  88. package/src/composables/useVirtualList.ts +1 -1
  89. package/src/index.ts +1 -0
  90. package/src/install.ts +9 -3
  91. package/src/version.ts +1 -1
  92. package/src/virtual/README.md +285 -0
  93. package/src/virtual/components/infiniteLoader/InfiniteLoader.test.ts +96 -0
  94. package/src/virtual/components/infiniteLoader/InfiniteLoader.vue +18 -0
  95. package/src/virtual/components/virtualGrid/VirtualGrid.vue +322 -0
  96. package/src/virtual/components/virtualGrid/types.ts +160 -0
  97. package/src/virtual/components/virtualList/VirtualList.test.ts +47 -0
  98. package/src/virtual/components/virtualList/VirtualList.vue +233 -0
  99. package/src/virtual/components/virtualList/isDynamicRowHeight.ts +13 -0
  100. package/src/virtual/components/virtualList/types.ts +127 -0
  101. package/src/virtual/components/virtualList/useDynamicRowHeight.test.ts +183 -0
  102. package/src/virtual/components/virtualList/useDynamicRowHeight.ts +147 -0
  103. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.test.ts +141 -0
  104. package/src/virtual/composables/infinite-loader/scanForUnloadedIndices.ts +82 -0
  105. package/src/virtual/composables/infinite-loader/types.ts +36 -0
  106. package/src/virtual/composables/infinite-loader/useInfiniteLoader.test.ts +236 -0
  107. package/src/virtual/composables/infinite-loader/useInfiniteLoader.ts +88 -0
  108. package/src/virtual/core/createCachedBounds.ts +72 -0
  109. package/src/virtual/core/getEstimatedSize.ts +29 -0
  110. package/src/virtual/core/getOffsetForIndex.ts +90 -0
  111. package/src/virtual/core/getStartStopIndices.test.ts +45 -0
  112. package/src/virtual/core/getStartStopIndices.ts +71 -0
  113. package/src/virtual/core/types.ts +17 -0
  114. package/src/virtual/core/useCachedBounds.ts +21 -0
  115. package/src/virtual/core/useIsRtl.ts +25 -0
  116. package/src/virtual/core/useItemSize.ts +34 -0
  117. package/src/virtual/core/useVirtualizer.ts +294 -0
  118. package/src/virtual/index.ts +25 -0
  119. package/src/virtual/test-utils/mockResizeObserver.ts +162 -0
  120. package/src/virtual/types.ts +3 -0
  121. package/src/virtual/utils/adjustScrollOffsetForRtl.ts +37 -0
  122. package/src/virtual/utils/areArraysEqual.ts +13 -0
  123. package/src/virtual/utils/assert.ts +10 -0
  124. package/src/virtual/utils/getRTLOffsetType.ts +51 -0
  125. package/src/virtual/utils/getScrollbarSize.ts +24 -0
  126. package/src/virtual/utils/isRtl.ts +13 -0
  127. package/src/virtual/utils/parseNumericStyleValue.ts +19 -0
  128. package/src/virtual/utils/shallowCompare.ts +29 -0
  129. package/volar.d.ts +3 -0
@@ -0,0 +1,90 @@
1
+ import type { Align } from '../types'
2
+ import { getEstimatedSize } from './getEstimatedSize'
3
+ import type { CachedBounds, SizeFunction } from './types'
4
+
5
+ export function getOffsetForIndex<Props extends object>({
6
+ align,
7
+ cachedBounds,
8
+ index,
9
+ itemCount,
10
+ itemSize,
11
+ containerScrollOffset,
12
+ containerSize,
13
+ }: {
14
+ align: Align;
15
+ cachedBounds: CachedBounds;
16
+ index: number;
17
+ itemCount: number;
18
+ itemSize: number | SizeFunction<Props>;
19
+ containerScrollOffset: number;
20
+ containerSize: number;
21
+ }) {
22
+ if (index < 0 || index >= itemCount) {
23
+ throw new RangeError(
24
+ `Invalid index specified: ${index}. Index ${index} is not within the range of 0 - ${itemCount - 1}`,
25
+ )
26
+ }
27
+
28
+ const estimatedTotalSize = getEstimatedSize({
29
+ cachedBounds,
30
+ itemCount,
31
+ itemSize,
32
+ })
33
+
34
+ const bounds = cachedBounds.get(index)
35
+ const maxOffset = Math.max(
36
+ 0,
37
+ Math.min(estimatedTotalSize - containerSize, bounds.scrollOffset),
38
+ )
39
+ const minOffset = Math.max(
40
+ 0,
41
+ bounds.scrollOffset - containerSize + bounds.size,
42
+ )
43
+
44
+ if (align === 'smart') {
45
+ if (
46
+ containerScrollOffset >= minOffset &&
47
+ containerScrollOffset <= maxOffset
48
+ ) {
49
+ align = 'auto'
50
+ } else {
51
+ align = 'center'
52
+ }
53
+ }
54
+
55
+ switch (align) {
56
+ case 'start': {
57
+ return maxOffset
58
+ }
59
+ case 'end': {
60
+ return minOffset
61
+ }
62
+ case 'center': {
63
+ if (bounds.scrollOffset <= containerSize / 2) {
64
+ // Too near the beginning to center-align
65
+ return 0
66
+ } else if (
67
+ bounds.scrollOffset + bounds.size / 2 >=
68
+ estimatedTotalSize - containerSize / 2
69
+ ) {
70
+ // Too near the end to center-align
71
+ return estimatedTotalSize - containerSize
72
+ } else {
73
+ return bounds.scrollOffset + bounds.size / 2 - containerSize / 2
74
+ }
75
+ }
76
+ case 'auto':
77
+ default: {
78
+ if (
79
+ containerScrollOffset >= minOffset &&
80
+ containerScrollOffset <= maxOffset
81
+ ) {
82
+ return containerScrollOffset
83
+ } else if (containerScrollOffset < minOffset) {
84
+ return minOffset
85
+ } else {
86
+ return maxOffset
87
+ }
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { getStartStopIndices } from './getStartStopIndices'
3
+ import { createCachedBounds } from './createCachedBounds'
4
+
5
+ describe('getStartStopIndices', () => {
6
+ it('calculates visible indices correctly', () => {
7
+ const cachedBounds = createCachedBounds({
8
+ itemCount: 100,
9
+ itemProps: {},
10
+ itemSize: 50,
11
+ })
12
+
13
+ const result = getStartStopIndices({
14
+ cachedBounds,
15
+ containerScrollOffset: 0,
16
+ containerSize: 400,
17
+ itemCount: 100,
18
+ overscanCount: 3,
19
+ })
20
+
21
+ expect(result.startIndexVisible).toBe(0)
22
+ expect(result.startIndexOverscan).toBe(0)
23
+ expect(result.stopIndexVisible).toBeGreaterThanOrEqual(0)
24
+ expect(result.stopIndexOverscan).toBeGreaterThanOrEqual(result.stopIndexVisible)
25
+ })
26
+
27
+ it('handles scrolled position', () => {
28
+ const cachedBounds = createCachedBounds({
29
+ itemCount: 100,
30
+ itemProps: {},
31
+ itemSize: 50,
32
+ })
33
+
34
+ const result = getStartStopIndices({
35
+ cachedBounds,
36
+ containerScrollOffset: 500,
37
+ containerSize: 400,
38
+ itemCount: 100,
39
+ overscanCount: 3,
40
+ })
41
+
42
+ expect(result.startIndexVisible).toBeGreaterThan(0)
43
+ expect(result.startIndexOverscan).toBeLessThanOrEqual(result.startIndexVisible)
44
+ })
45
+ })
@@ -0,0 +1,71 @@
1
+ import type { CachedBounds } from './types'
2
+
3
+ export function getStartStopIndices({
4
+ cachedBounds,
5
+ containerScrollOffset,
6
+ containerSize,
7
+ itemCount,
8
+ overscanCount,
9
+ }: {
10
+ cachedBounds: CachedBounds;
11
+ containerScrollOffset: number;
12
+ containerSize: number;
13
+ itemCount: number;
14
+ overscanCount: number;
15
+ }): {
16
+ startIndexVisible: number;
17
+ stopIndexVisible: number;
18
+ startIndexOverscan: number;
19
+ stopIndexOverscan: number;
20
+ } {
21
+ const maxIndex = itemCount - 1
22
+
23
+ let startIndexVisible = 0
24
+ let stopIndexVisible = -1
25
+ let startIndexOverscan = 0
26
+ let stopIndexOverscan = -1
27
+ let currentIndex = 0
28
+
29
+ while (currentIndex < maxIndex) {
30
+ const bounds = cachedBounds.get(currentIndex)
31
+
32
+ if (bounds.scrollOffset + bounds.size > containerScrollOffset) {
33
+ break
34
+ }
35
+
36
+ currentIndex++
37
+ }
38
+
39
+ startIndexVisible = currentIndex
40
+ startIndexOverscan = Math.max(0, startIndexVisible - overscanCount)
41
+
42
+ while (currentIndex < maxIndex) {
43
+ const bounds = cachedBounds.get(currentIndex)
44
+
45
+ if (
46
+ bounds.scrollOffset + bounds.size >=
47
+ containerScrollOffset + containerSize
48
+ ) {
49
+ break
50
+ }
51
+
52
+ currentIndex++
53
+ }
54
+
55
+ stopIndexVisible = Math.min(maxIndex, currentIndex)
56
+ stopIndexOverscan = Math.min(itemCount - 1, stopIndexVisible + overscanCount)
57
+
58
+ if (startIndexVisible < 0) {
59
+ startIndexVisible = 0
60
+ stopIndexVisible = -1
61
+ startIndexOverscan = 0
62
+ stopIndexOverscan = -1
63
+ }
64
+
65
+ return {
66
+ startIndexVisible,
67
+ stopIndexVisible,
68
+ startIndexOverscan,
69
+ stopIndexOverscan,
70
+ }
71
+ }
@@ -0,0 +1,17 @@
1
+ export type Bounds = {
2
+ size: number;
3
+ scrollOffset: number;
4
+ };
5
+
6
+ export type CachedBounds = {
7
+ get(index: number): Bounds;
8
+ set(index: number, bounds: Bounds): void;
9
+ size: number;
10
+ };
11
+
12
+ export type Direction = 'horizontal' | 'vertical';
13
+
14
+ export type SizeFunction<Props extends object> = (
15
+ index: number,
16
+ props: Props
17
+ ) => number;
@@ -0,0 +1,21 @@
1
+ import { computed, type ComputedRef } from 'vue'
2
+ import { createCachedBounds } from './createCachedBounds'
3
+ import type { CachedBounds, SizeFunction } from './types'
4
+
5
+ export function useCachedBounds<Props extends object>({
6
+ itemCount,
7
+ itemProps,
8
+ itemSize,
9
+ }: {
10
+ itemCount: number;
11
+ itemProps: Props;
12
+ itemSize: number | SizeFunction<Props>;
13
+ }): ComputedRef<CachedBounds> {
14
+ return computed(() =>
15
+ createCachedBounds({
16
+ itemCount,
17
+ itemProps,
18
+ itemSize,
19
+ }),
20
+ )
21
+ }
@@ -0,0 +1,25 @@
1
+ import { ref, watch, type Ref } from 'vue'
2
+ import { isRtl } from '../utils/isRtl'
3
+
4
+ export function useIsRtl(
5
+ element: Ref<HTMLElement | null>,
6
+ dir?: 'ltr' | 'rtl' | 'auto',
7
+ ) {
8
+ const value = ref(dir === 'rtl')
9
+
10
+ watch(
11
+ [element, () => dir],
12
+ ([el, direction]) => {
13
+ if (el) {
14
+ if (!direction || direction === 'auto') {
15
+ value.value = isRtl(el)
16
+ } else {
17
+ value.value = direction === 'rtl'
18
+ }
19
+ }
20
+ },
21
+ { immediate: true },
22
+ )
23
+
24
+ return value
25
+ }
@@ -0,0 +1,34 @@
1
+ import { assert } from '../utils/assert'
2
+ import type { SizeFunction } from './types'
3
+
4
+ export function useItemSize<Props extends object>({
5
+ containerSize,
6
+ itemSize: itemSizeProp,
7
+ }: {
8
+ containerSize: number;
9
+ itemSize: number | string | SizeFunction<Props>;
10
+ }) {
11
+ let itemSize: number | SizeFunction<Props>
12
+
13
+ switch (typeof itemSizeProp) {
14
+ case 'string': {
15
+ assert(
16
+ itemSizeProp.endsWith('%'),
17
+ `Invalid item size: "${itemSizeProp}"; string values must be percentages (e.g. "100%")`,
18
+ )
19
+ assert(
20
+ containerSize !== undefined,
21
+ 'Container size must be defined if a percentage item size is specified',
22
+ )
23
+
24
+ itemSize = (containerSize * parseInt(itemSizeProp)) / 100
25
+ break
26
+ }
27
+ default: {
28
+ itemSize = itemSizeProp
29
+ break
30
+ }
31
+ }
32
+
33
+ return itemSize
34
+ }
@@ -0,0 +1,294 @@
1
+ import {
2
+ computed,
3
+ isRef,
4
+ ref,
5
+ watch,
6
+ type ComputedRef,
7
+ type CSSProperties,
8
+ type Ref,
9
+ } from 'vue'
10
+ import { useEventListener, useResizeObserver } from '@vueuse/core'
11
+ import type { Align } from '../types'
12
+ import { parseNumericStyleValue } from '../utils/parseNumericStyleValue'
13
+ import { adjustScrollOffsetForRtl } from '../utils/adjustScrollOffsetForRtl'
14
+ import { shallowCompare } from '../utils/shallowCompare'
15
+ import { getEstimatedSize as getEstimatedSizeUtil } from './getEstimatedSize'
16
+ import { getOffsetForIndex } from './getOffsetForIndex'
17
+ import { getStartStopIndices as getStartStopIndicesUtil } from './getStartStopIndices'
18
+ import type { Direction, SizeFunction } from './types'
19
+ import { useCachedBounds } from './useCachedBounds'
20
+ import { useItemSize } from './useItemSize'
21
+
22
+ export function useVirtualizer<Props extends object>({
23
+ containerElement,
24
+ containerStyle,
25
+ defaultContainerSize = 0,
26
+ direction,
27
+ isRtl = false,
28
+ itemCount: itemCountInput,
29
+ itemProps: itemPropsInput,
30
+ itemSize: itemSizeProp,
31
+ onResize,
32
+ overscanCount,
33
+ }: {
34
+ containerElement: Ref<HTMLElement | null>;
35
+ containerStyle?: CSSProperties;
36
+ defaultContainerSize?: number;
37
+ direction: Direction;
38
+ isRtl?: boolean;
39
+ itemCount: number | Ref<number> | ComputedRef<number>;
40
+ itemProps: Props | ComputedRef<Props>;
41
+ itemSize: number | string | SizeFunction<Props> | Ref<number | string | SizeFunction<Props>> | ComputedRef<number | string | SizeFunction<Props>>;
42
+ onResize:
43
+ | ((
44
+ size: { height: number; width: number; },
45
+ prevSize: { height: number; width: number; }
46
+ ) => void)
47
+ | undefined;
48
+ overscanCount: number;
49
+ }) {
50
+ const itemCount = computed(() => {
51
+ return isRef(itemCountInput) ? itemCountInput.value : itemCountInput
52
+ })
53
+
54
+ const itemProps = computed(() => {
55
+ return isRef(itemPropsInput) ? itemPropsInput.value : itemPropsInput
56
+ })
57
+
58
+ const itemSizeNormalized = computed(() => {
59
+ return isRef(itemSizeProp) ? itemSizeProp.value : itemSizeProp
60
+ })
61
+
62
+ const styleHeight = computed(() => parseNumericStyleValue(containerStyle?.height))
63
+ const styleWidth = computed(() => parseNumericStyleValue(containerStyle?.width))
64
+
65
+ const sizeState = ref<{
66
+ height: number | undefined;
67
+ width: number | undefined;
68
+ }>({
69
+ height: direction === 'vertical' ? defaultContainerSize : undefined,
70
+ width: direction === 'horizontal' ? defaultContainerSize : undefined,
71
+ })
72
+
73
+ const observerDisabled = computed(
74
+ () =>
75
+ (direction === 'vertical' && styleHeight.value !== undefined) ||
76
+ (direction === 'horizontal' && styleWidth.value !== undefined),
77
+ )
78
+
79
+ useResizeObserver(
80
+ containerElement,
81
+ (entries) => {
82
+ if (observerDisabled.value) return
83
+
84
+ for (const entry of entries) {
85
+ const { contentRect } = entry
86
+ const prevState = sizeState.value
87
+
88
+ if (
89
+ prevState.height === contentRect.height &&
90
+ prevState.width === contentRect.width
91
+ ) {
92
+ return
93
+ }
94
+
95
+ sizeState.value = {
96
+ height: contentRect.height,
97
+ width: contentRect.width,
98
+ }
99
+ }
100
+ },
101
+ )
102
+
103
+ const size = computed(() => ({
104
+ height: styleHeight.value ?? sizeState.value.height,
105
+ width: styleWidth.value ?? sizeState.value.width,
106
+ }))
107
+
108
+ const prevSize = ref<{ height: number; width: number; }>({
109
+ height: 0,
110
+ width: 0,
111
+ })
112
+
113
+ watch(
114
+ size,
115
+ (newSize) => {
116
+ if (typeof onResize === 'function') {
117
+ const prev = prevSize.value
118
+ const height = newSize.height ?? 0
119
+ const width = newSize.width ?? 0
120
+
121
+ if (prev.height !== height || prev.width !== width) {
122
+ onResize({ height, width }, { ...prev })
123
+
124
+ prevSize.value = { height, width }
125
+ }
126
+ }
127
+ },
128
+ { immediate: true },
129
+ )
130
+
131
+ const containerSize = computed(() =>
132
+ direction === 'vertical'
133
+ ? size.value.height ?? defaultContainerSize
134
+ : size.value.width ?? defaultContainerSize,
135
+ )
136
+
137
+ const itemSize = computed(() =>
138
+ useItemSize({ containerSize: containerSize.value, itemSize: itemSizeNormalized.value }),
139
+ )
140
+
141
+ const cachedBounds = computed(() =>
142
+ useCachedBounds({
143
+ itemCount: itemCount.value,
144
+ itemProps: itemProps.value,
145
+ itemSize: itemSize.value,
146
+ }).value,
147
+ )
148
+
149
+ const getCellBounds = (index: number) => cachedBounds.value.get(index)
150
+
151
+ const indices = ref<{
152
+ startIndexVisible: number;
153
+ stopIndexVisible: number;
154
+ startIndexOverscan: number;
155
+ stopIndexOverscan: number;
156
+ }>(
157
+ getStartStopIndicesUtil({
158
+ cachedBounds: cachedBounds.value,
159
+ containerScrollOffset: 0,
160
+ containerSize: containerSize.value,
161
+ itemCount: itemCount.value,
162
+ overscanCount,
163
+ }),
164
+ )
165
+
166
+ const safeIndices = computed(() => {
167
+ const ind = indices.value
168
+
169
+ return {
170
+ startIndexVisible: Math.min(itemCount.value - 1, ind.startIndexVisible),
171
+ startIndexOverscan: Math.min(itemCount.value - 1, ind.startIndexOverscan),
172
+ stopIndexVisible: Math.min(itemCount.value - 1, ind.stopIndexVisible),
173
+ stopIndexOverscan: Math.min(itemCount.value - 1, ind.stopIndexOverscan),
174
+ }
175
+ })
176
+
177
+ const getEstimatedSize = computed(() =>
178
+ getEstimatedSizeUtil({
179
+ cachedBounds: cachedBounds.value,
180
+ itemCount: itemCount.value,
181
+ itemSize: itemSize.value,
182
+ }),
183
+ )
184
+
185
+ const getStartStopIndices = (scrollOffset: number) => {
186
+ const containerScrollOffset = adjustScrollOffsetForRtl({
187
+ containerElement: containerElement.value,
188
+ direction,
189
+ isRtl,
190
+ scrollOffset,
191
+ })
192
+
193
+ return getStartStopIndicesUtil({
194
+ cachedBounds: cachedBounds.value,
195
+ containerScrollOffset,
196
+ containerSize: containerSize.value,
197
+ itemCount: itemCount.value,
198
+ overscanCount,
199
+ })
200
+ }
201
+
202
+ watch(
203
+ [containerElement, () => direction, () => cachedBounds.value],
204
+ ([el]) => {
205
+ const scrollOffset =
206
+ (direction === 'vertical' ? el?.scrollTop : el?.scrollLeft) ?? 0
207
+
208
+ indices.value = getStartStopIndices(scrollOffset)
209
+ },
210
+ { immediate: true },
211
+ )
212
+
213
+ const onScroll = () => {
214
+ const el = containerElement.value
215
+
216
+ if (!el) return
217
+
218
+ const { scrollLeft, scrollTop } = el
219
+
220
+ const scrollOffset = adjustScrollOffsetForRtl({
221
+ containerElement: el,
222
+ direction,
223
+ isRtl,
224
+ scrollOffset: direction === 'vertical' ? scrollTop : scrollLeft,
225
+ })
226
+
227
+ const next = getStartStopIndicesUtil({
228
+ cachedBounds: cachedBounds.value,
229
+ containerScrollOffset: scrollOffset,
230
+ containerSize: containerSize.value,
231
+ itemCount: itemCount.value,
232
+ overscanCount,
233
+ })
234
+
235
+ if (!shallowCompare(next, indices.value)) {
236
+ indices.value = next
237
+ }
238
+ }
239
+
240
+ useEventListener(containerElement, 'scroll', onScroll)
241
+
242
+ const scrollToIndex = ({
243
+ align = 'auto',
244
+ containerScrollOffset,
245
+ index,
246
+ }: {
247
+ align?: Align;
248
+ containerScrollOffset: number;
249
+ index: number;
250
+ }) => {
251
+ let scrollOffset = getOffsetForIndex({
252
+ align,
253
+ cachedBounds: cachedBounds.value,
254
+ containerScrollOffset,
255
+ containerSize: containerSize.value,
256
+ index,
257
+ itemCount: itemCount.value,
258
+ itemSize: itemSize.value,
259
+ })
260
+
261
+ const el = containerElement.value
262
+
263
+ if (el) {
264
+ scrollOffset = adjustScrollOffsetForRtl({
265
+ containerElement: el,
266
+ direction,
267
+ isRtl,
268
+ scrollOffset,
269
+ })
270
+
271
+ if (typeof el.scrollTo !== 'function') {
272
+ const next = getStartStopIndices(scrollOffset)
273
+
274
+ if (!shallowCompare(indices.value, next)) {
275
+ indices.value = next
276
+ }
277
+ }
278
+
279
+ return scrollOffset
280
+ }
281
+
282
+ return undefined
283
+ }
284
+
285
+ return {
286
+ getCellBounds,
287
+ getEstimatedSize,
288
+ scrollToIndex,
289
+ startIndexOverscan: computed(() => safeIndices.value.startIndexOverscan),
290
+ startIndexVisible: computed(() => safeIndices.value.startIndexVisible),
291
+ stopIndexOverscan: computed(() => safeIndices.value.stopIndexOverscan),
292
+ stopIndexVisible: computed(() => safeIndices.value.stopIndexVisible),
293
+ }
294
+ }
@@ -0,0 +1,25 @@
1
+ export { default as XVirtualGrid } from './components/virtualGrid/VirtualGrid.vue'
2
+ export type {
3
+ CellSlotProps,
4
+ VirtualGridImperativeAPI,
5
+ VirtualGridProps,
6
+ } from './components/virtualGrid/types'
7
+
8
+ export { default as XVirtualList } from './components/virtualList/VirtualList.vue'
9
+ export { useDynamicRowHeight } from './components/virtualList/useDynamicRowHeight'
10
+ export type {
11
+ DynamicRowHeight,
12
+ VirtualListImperativeAPI,
13
+ VirtualListProps,
14
+ RowSlotProps,
15
+ } from './components/virtualList/types'
16
+
17
+ export { default as XInfiniteLoader } from './components/infiniteLoader/InfiniteLoader.vue'
18
+ export { useInfiniteLoader } from './composables/infinite-loader/useInfiniteLoader'
19
+ export type {
20
+ Indices,
21
+ OnRowsRendered,
22
+ InfiniteLoaderProps,
23
+ } from './composables/infinite-loader/types'
24
+
25
+ export { getScrollbarSize } from './utils/getScrollbarSize'