diff --git a/components/select/index.tsx b/components/select/index.tsx index 99e450f31..bb798aa62 100644 --- a/components/select/index.tsx +++ b/components/select/index.tsx @@ -61,7 +61,7 @@ const Select = defineComponent({ inheritAttrs: false, props: initDefaultProps(selectProps(), { listHeight: 256, - listItemHeight: 24, + listItemHeight: 32, }), SECRET_COMBOBOX_MODE_DO_NOT_USE, // emits: ['change', 'update:value', 'blur'], diff --git a/components/vc-virtual-list/List.tsx b/components/vc-virtual-list/List.tsx index fbf883ea3..beb83c283 100644 --- a/components/vc-virtual-list/List.tsx +++ b/components/vc-virtual-list/List.tsx @@ -1,5 +1,7 @@ import type { PropType, Component, CSSProperties } from 'vue'; import { + shallowRef, + toRaw, onMounted, onUpdated, ref, @@ -113,20 +115,36 @@ const List = defineComponent({ scrollTop: 0, scrollMoving: false, }); - - const mergedData = computed(() => { + const data = computed(() => { return props.data || EMPTY_DATA; }); - + const mergedData = shallowRef([]); + watch( + data, + () => { + mergedData.value = toRaw(data.value).slice(); + }, + { immediate: true }, + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const itemKey = shallowRef((_item: Record) => undefined); + watch( + () => props.itemKey, + val => { + if (typeof val === 'function') { + itemKey.value = val; + } else { + itemKey.value = item => item?.[val]; + } + }, + { immediate: true }, + ); const componentRef = ref(); const fillerInnerRef = ref(); const scrollBarRef = ref(); // Hack on scrollbar to enable flash call // =============================== Item Key =============================== const getKey = (item: Record) => { - if (typeof props.itemKey === 'function') { - return props.itemKey(item); - } - return item?.[props.itemKey]; + return itemKey.value(item); }; const sharedConfig = { @@ -217,7 +235,6 @@ const List = defineComponent({ () => state.scrollTop, mergedData, updatedMark, - heights, () => props.height, offsetHeight, ], @@ -232,21 +249,27 @@ const List = defineComponent({ let endIndex: number | undefined; const dataLen = mergedData.value.length; const data = mergedData.value; + const scrollTop = state.scrollTop; + const { itemHeight, height } = props; + const scrollTopHeight = scrollTop + height; + for (let i = 0; i < dataLen; i += 1) { const item = data[i]; const key = getKey(item); - const cacheHeight = heights.value[key]; - const currentItemBottom = - itemTop + (cacheHeight === undefined ? props.itemHeight! : cacheHeight); + let cacheHeight = heights.get(key); + if (cacheHeight === undefined) { + cacheHeight = itemHeight; + } + const currentItemBottom = itemTop + cacheHeight; - if (currentItemBottom >= state.scrollTop && startIndex === undefined) { + if (startIndex === undefined && currentItemBottom >= scrollTop) { startIndex = i; startOffset = itemTop; } // Check item bottom in the range. We will render additional one item for motion usage - if (currentItemBottom > state.scrollTop + props.height! && endIndex === undefined) { + if (endIndex === undefined && currentItemBottom > scrollTopHeight) { endIndex = i; } diff --git a/components/vc-virtual-list/hooks/useHeights.tsx b/components/vc-virtual-list/hooks/useHeights.tsx index 70bdca9f8..634afc293 100644 --- a/components/vc-virtual-list/hooks/useHeights.tsx +++ b/components/vc-virtual-list/hooks/useHeights.tsx @@ -1,20 +1,20 @@ -import type { VNodeProps, ComputedRef, Ref } from 'vue'; -import { shallowRef, watch, ref } from 'vue'; +import type { VNodeProps, Ref, ShallowRef } from 'vue'; +import { watch, ref } from 'vue'; import type { GetKey } from '../interface'; -type CacheMap = Ref>; +export type CacheMap = Map; export default function useHeights( - mergedData: ComputedRef, + mergedData: ShallowRef, getKey: GetKey, onItemAdd?: ((item: T) => void) | null, onItemRemove?: ((item: T) => void) | null, ): [(item: T, instance: HTMLElement) => void, () => void, CacheMap, Ref] { const instance = new Map(); - const heights = shallowRef({}); + let heights = new Map(); const updatedMark = ref(Symbol('update')); watch(mergedData, () => { - heights.value = {}; + heights = new Map(); updatedMark.value = Symbol('update'); }); let heightUpdateId = 0; @@ -28,10 +28,10 @@ export default function useHeights( instance.forEach((element, key) => { if (element && element.offsetParent) { const { offsetHeight } = element; - if (heights.value[key!] !== offsetHeight) { + if (heights.get(key) !== offsetHeight) { //changed = true; updatedMark.value = Symbol('update'); - heights.value[key!] = element.offsetHeight; + heights.set(key, element.offsetHeight); } } }); diff --git a/components/vc-virtual-list/hooks/useScrollTo.tsx b/components/vc-virtual-list/hooks/useScrollTo.tsx index dd6bbb25a..8db88a123 100644 --- a/components/vc-virtual-list/hooks/useScrollTo.tsx +++ b/components/vc-virtual-list/hooks/useScrollTo.tsx @@ -1,12 +1,12 @@ -import type { Data } from '../../_util/type'; -import type { ComputedRef, Ref } from 'vue'; +import type { ShallowRef, Ref } from 'vue'; import raf from '../../_util/raf'; import type { GetKey } from '../interface'; +import type { CacheMap } from './useHeights'; export default function useScrollTo( containerRef: Ref, - mergedData: ComputedRef, - heights: Ref, + mergedData: ShallowRef, + heights: CacheMap, props, getKey: GetKey, collectHeight: () => void, @@ -58,11 +58,10 @@ export default function useScrollTo( let itemBottom = 0; const maxLen = Math.min(data.length, index); - for (let i = 0; i <= maxLen; i += 1) { const key = getKey(data[i]); itemTop = stackTop; - const cacheHeight = heights.value[key!]; + const cacheHeight = heights.get(key); itemBottom = itemTop + (cacheHeight === undefined ? itemHeight : cacheHeight); stackTop = itemBottom; @@ -71,7 +70,7 @@ export default function useScrollTo( needCollectHeight = true; } } - + const scrollTop = containerRef.value.scrollTop; // Scroll to let targetTop: number | null = null; @@ -84,7 +83,6 @@ export default function useScrollTo( break; default: { - const { scrollTop } = containerRef.value; const scrollBottom = scrollTop + height; if (itemTop < scrollTop) { newTargetAlign = 'top'; @@ -94,7 +92,7 @@ export default function useScrollTo( } } - if (targetTop !== null && targetTop !== containerRef.value.scrollTop) { + if (targetTop !== null && targetTop !== scrollTop) { syncScrollTop(targetTop); } }