@v-c/virtual-list 0.0.1 → 1.0.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/bump.config.ts +6 -0
- package/dist/Filler.cjs +51 -1
- package/dist/Filler.js +47 -56
- package/dist/Item.cjs +26 -1
- package/dist/Item.js +23 -26
- package/dist/List.cjs +408 -1
- package/dist/List.d.ts +45 -9
- package/dist/List.js +403 -274
- package/dist/ScrollBar.cjs +259 -1
- package/dist/ScrollBar.d.ts +3 -97
- package/dist/ScrollBar.js +254 -191
- package/dist/_virtual/rolldown_runtime.cjs +21 -0
- package/dist/hooks/useDiffItem.cjs +19 -1
- package/dist/hooks/useDiffItem.js +16 -20
- package/dist/hooks/useFrameWheel.cjs +63 -1
- package/dist/hooks/useFrameWheel.js +60 -51
- package/dist/hooks/useGetSize.cjs +29 -1
- package/dist/hooks/useGetSize.d.ts +2 -2
- package/dist/hooks/useGetSize.js +27 -23
- package/dist/hooks/useHeights.cjs +66 -1
- package/dist/hooks/useHeights.d.ts +1 -1
- package/dist/hooks/useHeights.js +62 -41
- package/dist/hooks/useMobileTouchMove.cjs +82 -1
- package/dist/hooks/useMobileTouchMove.js +79 -43
- package/dist/hooks/useOriginScroll.cjs +23 -1
- package/dist/hooks/useOriginScroll.js +20 -16
- package/dist/hooks/useScrollDrag.cjs +83 -1
- package/dist/hooks/useScrollDrag.js +77 -48
- package/dist/hooks/useScrollTo.cjs +97 -0
- package/dist/hooks/useScrollTo.d.ts +19 -0
- package/dist/hooks/useScrollTo.js +94 -0
- package/dist/index.cjs +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -4
- package/dist/interface.cjs +0 -1
- package/dist/interface.d.ts +1 -1
- package/dist/interface.js +0 -1
- package/dist/utils/CacheMap.cjs +25 -1
- package/dist/utils/CacheMap.d.ts +1 -1
- package/dist/utils/CacheMap.js +23 -28
- package/dist/utils/isFirefox.cjs +4 -1
- package/dist/utils/isFirefox.js +2 -4
- package/dist/utils/scrollbarUtil.cjs +8 -1
- package/dist/utils/scrollbarUtil.js +7 -6
- package/docs/animate.less +31 -0
- package/docs/animate.vue +159 -0
- package/docs/basic.vue +2 -1
- package/docs/nest.vue +1 -1
- package/docs/switch.vue +2 -1
- package/docs/virtual-list.stories.vue +4 -0
- package/package.json +16 -14
- package/src/Filler.tsx +2 -1
- package/src/Item.tsx +2 -1
- package/src/List.tsx +189 -124
- package/src/ScrollBar.tsx +33 -44
- package/src/hooks/useDiffItem.ts +3 -2
- package/src/hooks/useFrameWheel.ts +2 -1
- package/src/hooks/useGetSize.ts +5 -4
- package/src/hooks/useHeights.ts +7 -6
- package/src/hooks/useMobileTouchMove.ts +2 -1
- package/src/hooks/useOriginScroll.ts +2 -1
- package/src/hooks/useScrollDrag.ts +2 -1
- package/src/hooks/useScrollTo.tsx +184 -0
- package/src/index.ts +1 -1
- package/tsconfig.json +7 -0
- package/vitest.config.ts +1 -1
package/src/ScrollBar.tsx
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CSSProperties } from 'vue'
|
|
2
|
+
import raf from '@v-c/util/dist/raf'
|
|
3
|
+
import { computed, defineComponent, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
|
4
|
+
|
|
5
|
+
export type ScrollBarDirectionType = 'ltr' | 'rtl'
|
|
2
6
|
|
|
3
7
|
export interface ScrollBarProps {
|
|
4
8
|
prefixCls: string
|
|
@@ -28,23 +32,8 @@ function getPageXY(
|
|
|
28
32
|
return obj[horizontal ? 'pageX' : 'pageY'] - window[horizontal ? 'scrollX' : 'scrollY']
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
export default defineComponent({
|
|
35
|
+
export default defineComponent<ScrollBarProps>({
|
|
32
36
|
name: 'ScrollBar',
|
|
33
|
-
props: {
|
|
34
|
-
prefixCls: { type: String, required: true },
|
|
35
|
-
scrollOffset: { type: Number, required: true },
|
|
36
|
-
scrollRange: { type: Number, required: true },
|
|
37
|
-
rtl: { type: Boolean, default: false },
|
|
38
|
-
onScroll: { type: Function as PropType<(scrollOffset: number, horizontal?: boolean) => void>, required: true },
|
|
39
|
-
onStartMove: { type: Function as PropType<() => void>, required: true },
|
|
40
|
-
onStopMove: { type: Function as PropType<() => void>, required: true },
|
|
41
|
-
horizontal: { type: Boolean, default: false },
|
|
42
|
-
style: Object as PropType<CSSProperties>,
|
|
43
|
-
thumbStyle: Object as PropType<CSSProperties>,
|
|
44
|
-
spinSize: { type: Number, required: true },
|
|
45
|
-
containerSize: { type: Number, required: true },
|
|
46
|
-
showScrollBar: { type: [Boolean, String] as PropType<boolean | 'optional'> },
|
|
47
|
-
},
|
|
48
37
|
setup(props, { expose }) {
|
|
49
38
|
const dragging = ref(false)
|
|
50
39
|
const pageXY = ref<number | null>(null)
|
|
@@ -52,13 +41,11 @@ export default defineComponent({
|
|
|
52
41
|
|
|
53
42
|
const isLTR = computed(() => !props.rtl)
|
|
54
43
|
|
|
55
|
-
// Refs
|
|
44
|
+
// ========================= Refs =========================
|
|
56
45
|
const scrollbarRef = shallowRef<HTMLDivElement>()
|
|
57
46
|
const thumbRef = shallowRef<HTMLDivElement>()
|
|
58
47
|
|
|
59
|
-
// Visible
|
|
60
|
-
// When showScrollBar is 'optional', start as visible (true)
|
|
61
|
-
// When showScrollBar is true/false, use that value
|
|
48
|
+
// ======================= Visible ========================
|
|
62
49
|
const visible = ref(props.showScrollBar === 'optional' ? true : props.showScrollBar)
|
|
63
50
|
let visibleTimeout: ReturnType<typeof setTimeout> | null = null
|
|
64
51
|
|
|
@@ -74,11 +61,11 @@ export default defineComponent({
|
|
|
74
61
|
}, 3000)
|
|
75
62
|
}
|
|
76
63
|
|
|
77
|
-
// Range
|
|
64
|
+
// ======================== Range =========================
|
|
78
65
|
const enableScrollRange = computed(() => props.scrollRange - props.containerSize || 0)
|
|
79
66
|
const enableOffsetRange = computed(() => props.containerSize - props.spinSize || 0)
|
|
80
67
|
|
|
81
|
-
// Top
|
|
68
|
+
// ========================= Top ==========================
|
|
82
69
|
const top = computed(() => {
|
|
83
70
|
if (props.scrollOffset === 0 || enableScrollRange.value === 0) {
|
|
84
71
|
return 0
|
|
@@ -87,7 +74,7 @@ export default defineComponent({
|
|
|
87
74
|
return ptg * enableOffsetRange.value
|
|
88
75
|
})
|
|
89
76
|
|
|
90
|
-
//
|
|
77
|
+
// ======================== Thumb =========================
|
|
91
78
|
const stateRef = shallowRef({
|
|
92
79
|
top: top.value,
|
|
93
80
|
dragging: dragging.value,
|
|
@@ -114,12 +101,16 @@ export default defineComponent({
|
|
|
114
101
|
pageXY.value = getPageXY(e, props.horizontal || false)
|
|
115
102
|
startTop.value = stateRef.value.top
|
|
116
103
|
|
|
117
|
-
props
|
|
104
|
+
props?.onStartMove?.()
|
|
118
105
|
e.stopPropagation()
|
|
119
106
|
e.preventDefault()
|
|
120
107
|
}
|
|
121
108
|
|
|
122
|
-
// Effect
|
|
109
|
+
// ======================== Effect ========================
|
|
110
|
+
|
|
111
|
+
// React make event as passive, but we need to preventDefault
|
|
112
|
+
// Add event on dom directly instead.
|
|
113
|
+
// ref: https://github.com/facebook/react/issues/9809
|
|
123
114
|
onMounted(() => {
|
|
124
115
|
const onScrollbarTouchStart = (e: TouchEvent) => {
|
|
125
116
|
e.preventDefault()
|
|
@@ -140,7 +131,7 @@ export default defineComponent({
|
|
|
140
131
|
})
|
|
141
132
|
|
|
142
133
|
// Effect: Handle dragging
|
|
143
|
-
watch(dragging, (isDragging) => {
|
|
134
|
+
watch(dragging, (isDragging, _O, onCleanup) => {
|
|
144
135
|
if (isDragging) {
|
|
145
136
|
let moveRafId: number | null = null
|
|
146
137
|
|
|
@@ -150,9 +141,7 @@ export default defineComponent({
|
|
|
150
141
|
pageY: statePageY,
|
|
151
142
|
startTop: stateStartTop,
|
|
152
143
|
} = stateRef.value
|
|
153
|
-
|
|
154
|
-
if (moveRafId)
|
|
155
|
-
cancelAnimationFrame(moveRafId)
|
|
144
|
+
raf.cancel(moveRafId!)
|
|
156
145
|
|
|
157
146
|
const rect = scrollbarRef.value!.getBoundingClientRect()
|
|
158
147
|
const scale = props.containerSize / (props.horizontal ? rect.width : rect.height)
|
|
@@ -177,8 +166,8 @@ export default defineComponent({
|
|
|
177
166
|
newScrollTop = Math.max(newScrollTop, 0)
|
|
178
167
|
newScrollTop = Math.min(newScrollTop, tmpEnableScrollRange)
|
|
179
168
|
|
|
180
|
-
moveRafId =
|
|
181
|
-
props
|
|
169
|
+
moveRafId = raf(() => {
|
|
170
|
+
props?.onScroll?.(newScrollTop, props.horizontal)
|
|
182
171
|
})
|
|
183
172
|
}
|
|
184
173
|
}
|
|
@@ -193,26 +182,25 @@ export default defineComponent({
|
|
|
193
182
|
window.addEventListener('mouseup', onMouseUp, { passive: true } as any)
|
|
194
183
|
window.addEventListener('touchend', onMouseUp, { passive: true } as any)
|
|
195
184
|
|
|
196
|
-
|
|
185
|
+
onCleanup(() => {
|
|
197
186
|
window.removeEventListener('mousemove', onMouseMove)
|
|
198
187
|
window.removeEventListener('touchmove', onMouseMove)
|
|
199
188
|
window.removeEventListener('mouseup', onMouseUp)
|
|
200
189
|
window.removeEventListener('touchend', onMouseUp)
|
|
201
190
|
|
|
202
|
-
|
|
203
|
-
cancelAnimationFrame(moveRafId)
|
|
191
|
+
raf.cancel(moveRafId!)
|
|
204
192
|
})
|
|
205
193
|
}
|
|
206
194
|
})
|
|
207
195
|
|
|
208
196
|
// Effect: Delay hidden on scroll offset change
|
|
209
|
-
watch(() => props.scrollOffset, () => {
|
|
197
|
+
watch(() => props.scrollOffset, (_n, _o, onCleanup) => {
|
|
210
198
|
delayHidden()
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
199
|
+
onCleanup(() => {
|
|
200
|
+
if (visibleTimeout) {
|
|
201
|
+
clearTimeout(visibleTimeout)
|
|
202
|
+
}
|
|
203
|
+
})
|
|
216
204
|
})
|
|
217
205
|
|
|
218
206
|
// Imperative handle
|
|
@@ -221,7 +209,8 @@ export default defineComponent({
|
|
|
221
209
|
})
|
|
222
210
|
|
|
223
211
|
return () => {
|
|
224
|
-
const
|
|
212
|
+
const { prefixCls, horizontal } = props
|
|
213
|
+
const scrollbarPrefixCls = `${prefixCls}-scrollbar`
|
|
225
214
|
|
|
226
215
|
const containerStyle: CSSProperties = {
|
|
227
216
|
position: 'absolute',
|
|
@@ -271,8 +260,8 @@ export default defineComponent({
|
|
|
271
260
|
class={[
|
|
272
261
|
scrollbarPrefixCls,
|
|
273
262
|
{
|
|
274
|
-
[`${scrollbarPrefixCls}-horizontal`]:
|
|
275
|
-
[`${scrollbarPrefixCls}-vertical`]: !
|
|
263
|
+
[`${scrollbarPrefixCls}-horizontal`]: horizontal,
|
|
264
|
+
[`${scrollbarPrefixCls}-vertical`]: !horizontal,
|
|
276
265
|
[`${scrollbarPrefixCls}-visible`]: visible.value,
|
|
277
266
|
},
|
|
278
267
|
]}
|
package/src/hooks/useDiffItem.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import { ref, watch } from 'vue'
|
|
2
3
|
|
|
3
4
|
export default function useDiffItem<T>(data: Ref<T[]>, getKey: (item: T) => any): Ref<T | undefined> {
|
|
4
5
|
const prevDataRef = ref<T[]>([])
|
|
@@ -13,7 +14,7 @@ export default function useDiffItem<T>(data: Ref<T[]>, getKey: (item: T) => any)
|
|
|
13
14
|
// Find added item
|
|
14
15
|
const addedItem = newData.find((item) => {
|
|
15
16
|
const key = getKey(item)
|
|
16
|
-
return !prevData.some(prevItem => getKey(prevItem) === key)
|
|
17
|
+
return !prevData.some(prevItem => getKey(prevItem as any) === key)
|
|
17
18
|
})
|
|
18
19
|
|
|
19
20
|
diffItem.value = addedItem
|
package/src/hooks/useGetSize.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue'
|
|
1
2
|
import type { GetKey } from '../interface'
|
|
2
3
|
import type CacheMap from '../utils/CacheMap'
|
|
3
|
-
import { computed
|
|
4
|
+
import { computed } from 'vue'
|
|
4
5
|
|
|
5
6
|
export function useGetSize<T>(
|
|
6
7
|
mergedData: Ref<T[]>,
|
|
7
8
|
getKey: GetKey<T>,
|
|
8
9
|
heights: CacheMap,
|
|
9
|
-
itemHeight: number
|
|
10
|
+
itemHeight: Ref<number>,
|
|
10
11
|
): ComputedRef<(startKey: any, endKey?: any) => { top: number, bottom: number }> {
|
|
11
12
|
return computed(() => {
|
|
12
13
|
return (startKey: any, endKey?: any) => {
|
|
@@ -25,14 +26,14 @@ export function useGetSize<T>(
|
|
|
25
26
|
for (let i = 0; i < topIndex; i += 1) {
|
|
26
27
|
const key = getKey(mergedData.value[i])
|
|
27
28
|
const cacheHeight = heights.get(key)
|
|
28
|
-
top += cacheHeight === undefined ? itemHeight : cacheHeight
|
|
29
|
+
top += cacheHeight === undefined ? itemHeight.value : cacheHeight
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
let bottom = 0
|
|
32
33
|
for (let i = mergedData.value.length - 1; i > bottomIndex; i -= 1) {
|
|
33
34
|
const key = getKey(mergedData.value[i])
|
|
34
35
|
const cacheHeight = heights.get(key)
|
|
35
|
-
bottom += cacheHeight === undefined ? itemHeight : cacheHeight
|
|
36
|
+
bottom += cacheHeight === undefined ? itemHeight.value : cacheHeight
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
return {
|
package/src/hooks/useHeights.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Key } from '@v-c/util/dist/type'
|
|
2
|
+
import type { Ref } from 'vue'
|
|
2
3
|
import type { GetKey } from '../interface'
|
|
3
|
-
import { onUnmounted,
|
|
4
|
+
import { onUnmounted, reactive, ref } from 'vue'
|
|
4
5
|
import CacheMap from '../utils/CacheMap'
|
|
5
6
|
|
|
6
7
|
function parseNumber(value: string) {
|
|
@@ -17,10 +18,10 @@ export default function useHeights<T>(
|
|
|
17
18
|
collectHeight: (sync?: boolean) => void,
|
|
18
19
|
cacheMap: CacheMap,
|
|
19
20
|
updatedMark: Ref<number>,
|
|
20
|
-
|
|
21
|
+
] {
|
|
21
22
|
const updatedMark = ref(0)
|
|
22
23
|
const instanceRef = ref(new Map<Key, HTMLElement>())
|
|
23
|
-
const heightsRef =
|
|
24
|
+
const heightsRef = reactive(new CacheMap())
|
|
24
25
|
|
|
25
26
|
const promiseIdRef = ref<number>(0)
|
|
26
27
|
|
|
@@ -43,8 +44,8 @@ export default function useHeights<T>(
|
|
|
43
44
|
const marginBottomNum = parseNumber(marginBottom)
|
|
44
45
|
const totalHeight = offsetHeight + marginTopNum + marginBottomNum
|
|
45
46
|
|
|
46
|
-
if (heightsRef.
|
|
47
|
-
heightsRef.
|
|
47
|
+
if (heightsRef.get(key) !== totalHeight) {
|
|
48
|
+
heightsRef.set(key, totalHeight)
|
|
48
49
|
changed = true
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -102,5 +103,5 @@ export default function useHeights<T>(
|
|
|
102
103
|
cancelRaf()
|
|
103
104
|
})
|
|
104
105
|
|
|
105
|
-
return [setInstanceRef, collectHeight, heightsRef
|
|
106
|
+
return [setInstanceRef, collectHeight, heightsRef, updatedMark]
|
|
106
107
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { Key } from '@v-c/util/dist/type'
|
|
2
|
+
import type { Ref } from 'vue'
|
|
3
|
+
import type { GetKey } from '../interface.ts'
|
|
4
|
+
import type CacheMap from '../utils/CacheMap.ts'
|
|
5
|
+
import { warning } from '@v-c/util'
|
|
6
|
+
import raf from '@v-c/util/dist/raf'
|
|
7
|
+
import { shallowRef, watch } from 'vue'
|
|
8
|
+
|
|
9
|
+
const MAX_TIMES = 10
|
|
10
|
+
|
|
11
|
+
export type ScrollAlign = 'top' | 'bottom' | 'auto'
|
|
12
|
+
|
|
13
|
+
export interface ScrollPos {
|
|
14
|
+
left?: number
|
|
15
|
+
top?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type ScrollTarget = {
|
|
19
|
+
index: number
|
|
20
|
+
align?: ScrollAlign
|
|
21
|
+
offset?: number
|
|
22
|
+
} | {
|
|
23
|
+
key: Key
|
|
24
|
+
align?: ScrollAlign
|
|
25
|
+
offset?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function useScrollTo(
|
|
29
|
+
containerRef: Ref<HTMLDivElement>,
|
|
30
|
+
data: Ref<any[]>,
|
|
31
|
+
heights: CacheMap,
|
|
32
|
+
itemHeight: Ref<number>,
|
|
33
|
+
getKey: GetKey<any>,
|
|
34
|
+
collectHeight: () => void,
|
|
35
|
+
syncScrollTop: (newTop: number) => void,
|
|
36
|
+
triggerFlash: () => void,
|
|
37
|
+
): (arg: number | ScrollTarget) => void {
|
|
38
|
+
const scrollRef = shallowRef<number>()
|
|
39
|
+
|
|
40
|
+
const syncState = shallowRef<{
|
|
41
|
+
times: number
|
|
42
|
+
index: number
|
|
43
|
+
offset: number
|
|
44
|
+
originAlign: ScrollAlign
|
|
45
|
+
targetAlign?: 'top' | 'bottom'
|
|
46
|
+
lastTop?: number
|
|
47
|
+
} | null>(null)
|
|
48
|
+
|
|
49
|
+
// ========================== Sync Scroll ==========================
|
|
50
|
+
watch(
|
|
51
|
+
[syncState, containerRef],
|
|
52
|
+
() => {
|
|
53
|
+
if (syncState.value && syncState.value.times < MAX_TIMES) {
|
|
54
|
+
// Never reach
|
|
55
|
+
if (!containerRef.value) {
|
|
56
|
+
syncState.value = { ...syncState.value }
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
collectHeight()
|
|
60
|
+
const { targetAlign, originAlign, index, offset } = syncState.value
|
|
61
|
+
|
|
62
|
+
const height = containerRef.value.clientHeight
|
|
63
|
+
let needCollectHeight = false
|
|
64
|
+
let newTargetAlign: 'top' | 'bottom' | null = targetAlign ?? null
|
|
65
|
+
let targetTop: number | null = null
|
|
66
|
+
// Go to next frame if height not exist
|
|
67
|
+
if (height) {
|
|
68
|
+
const mergedAlign = targetAlign || originAlign
|
|
69
|
+
// Get top & bottom
|
|
70
|
+
let stackTop = 0
|
|
71
|
+
let itemTop = 0
|
|
72
|
+
let itemBottom = 0
|
|
73
|
+
|
|
74
|
+
const maxLen = Math.min(data.value.length - 1, index)
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i <= maxLen; i += 1) {
|
|
77
|
+
const key = getKey(data.value[i])
|
|
78
|
+
itemTop = stackTop
|
|
79
|
+
const cacheHeight = heights.get(key)
|
|
80
|
+
itemBottom = itemTop + (cacheHeight === undefined ? itemHeight.value : cacheHeight)
|
|
81
|
+
|
|
82
|
+
stackTop = itemBottom
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if need sync height (visible range has item not record height)
|
|
86
|
+
let leftHeight = mergedAlign === 'top' ? offset : height - offset
|
|
87
|
+
for (let i = maxLen; i >= 0; i -= 1) {
|
|
88
|
+
const key = getKey(data.value[i])
|
|
89
|
+
const cacheHeight = heights.get(key)
|
|
90
|
+
|
|
91
|
+
if (cacheHeight === undefined) {
|
|
92
|
+
needCollectHeight = true
|
|
93
|
+
break
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
leftHeight -= cacheHeight
|
|
97
|
+
if (leftHeight <= 0) {
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Scroll to
|
|
103
|
+
switch (mergedAlign) {
|
|
104
|
+
case 'top':
|
|
105
|
+
targetTop = itemTop - offset
|
|
106
|
+
break
|
|
107
|
+
case 'bottom':
|
|
108
|
+
targetTop = itemBottom - height + offset
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
default: {
|
|
112
|
+
const { scrollTop } = containerRef.value
|
|
113
|
+
const scrollBottom = scrollTop + height
|
|
114
|
+
if (itemTop < scrollTop) {
|
|
115
|
+
newTargetAlign = 'top'
|
|
116
|
+
}
|
|
117
|
+
else if (itemBottom > scrollBottom) {
|
|
118
|
+
newTargetAlign = 'bottom'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (targetTop !== null) {
|
|
123
|
+
syncScrollTop(targetTop)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// One more time for sync
|
|
127
|
+
if (targetTop !== syncState.value.lastTop) {
|
|
128
|
+
needCollectHeight = true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (needCollectHeight) {
|
|
133
|
+
syncState.value = {
|
|
134
|
+
...syncState.value,
|
|
135
|
+
times: syncState.value.times + 1,
|
|
136
|
+
targetAlign: newTargetAlign as any,
|
|
137
|
+
lastTop: targetTop as any,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (process.env.NODE_ENV !== 'production' && syncState.value?.times === MAX_TIMES) {
|
|
142
|
+
warning(
|
|
143
|
+
false,
|
|
144
|
+
'Seems `scrollTo` with `rc-virtual-list` reach the max limitation. Please fire issue for us. Thanks.',
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
immediate: true,
|
|
150
|
+
flush: 'post',
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// =========================== Scroll To ===========================
|
|
155
|
+
return (arg) => {
|
|
156
|
+
if (arg === null || arg === undefined) {
|
|
157
|
+
triggerFlash()
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
// Normalize target
|
|
161
|
+
raf.cancel(scrollRef.value!)
|
|
162
|
+
if (typeof arg === 'number') {
|
|
163
|
+
syncScrollTop(arg)
|
|
164
|
+
}
|
|
165
|
+
else if (arg && typeof arg === 'object') {
|
|
166
|
+
let index: number
|
|
167
|
+
const { align } = arg
|
|
168
|
+
if ('index' in arg) {
|
|
169
|
+
({ index } = arg)
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
index = data.value.findIndex(item => getKey(item) === arg.key)
|
|
173
|
+
}
|
|
174
|
+
const { offset = 0 } = arg
|
|
175
|
+
|
|
176
|
+
syncState.value = {
|
|
177
|
+
times: 0,
|
|
178
|
+
index,
|
|
179
|
+
offset,
|
|
180
|
+
originAlign: align!,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
package/src/index.ts
CHANGED
package/tsconfig.json
ADDED
package/vitest.config.ts
CHANGED