Skip to content

Commit

Permalink
feat(VVirtualScroll): add itemKey prop
Browse files Browse the repository at this point in the history
closes #20809
  • Loading branch information
KaelWD committed Jan 6, 2025
1 parent 8cb5dab commit fabc511
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 13 deletions.
2 changes: 1 addition & 1 deletion packages/api-generator/src/locale/en/VVirtualScroll.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"props": {
"height": "Height of the component as a css value/",
"itemKey": "Required when using **dynamic-item-height** together with object items. Should point to a property with a unique value for each item.",
"itemKey": "Should point to a property with a unique value for each item, if not set then item index will be used as a key which may result in unnecessary re-renders.",
"items": "The array of items to display.",
"renderless": "Disables default component rendering functionality."
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ export const VAutocomplete = genericComponent<new <
<VListItem key="no-data" title={ t(props.noDataText) } />
))}

<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value }>
<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value } itemKey="value">
{ ({ item, index, itemRef }) => {
const itemProps = mergeProps(item.props, {
ref: itemRef,
Expand Down
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VCombobox/VCombobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ export const VCombobox = genericComponent<new <
<VListItem key="no-data" title={ t(props.noDataText) } />
))}

<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value }>
<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value } itemKey="value">
{ ({ item, index, itemRef }) => {
const itemProps = mergeProps(item.props, {
ref: itemRef,
Expand Down
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VSelect/VSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ export const VSelect = genericComponent<new <
<VListItem key="no-data" title={ t(props.noDataText) } />
))}

<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value }>
<VVirtualScroll ref={ vVirtualScrollRef } renderless items={ displayItems.value } itemKey="value">
{ ({ item, index, itemRef }) => {
const itemProps = mergeProps(item.props, {
ref: itemRef,
Expand Down
27 changes: 18 additions & 9 deletions packages/vuetify/src/composables/virtual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { useResizeObserver } from '@/composables/resizeObserver'

// Utilities
import { computed, nextTick, onScopeDispose, ref, shallowRef, watch, watchEffect } from 'vue'
import { clamp, debounce, IN_BROWSER, isObject, propsFactory } from '@/util'
import { clamp, debounce, getPropertyFromItem, IN_BROWSER, propsFactory } from '@/util'

// Types
import type { Ref } from 'vue'
import type { PropType, Ref } from 'vue'
import type { SelectItemKey } from '@/util'

const UP = -1
const DOWN = 1
Expand All @@ -16,15 +17,20 @@ const DOWN = 1
const BUFFER_PX = 100

type VirtualProps = {
itemHeight?: number | string
height?: number | string
itemHeight: number | string | null | undefined
itemKey: SelectItemKey
height: number | string | undefined
}

export const makeVirtualProps = propsFactory({
itemHeight: {
type: [Number, String],
default: null,
},
itemKey: {
type: [String, Array, Function] as PropType<SelectItemKey>,
default: null,
},
height: [Number, String],
}, 'virtual')

Expand Down Expand Up @@ -235,11 +241,14 @@ export function useVirtual <T> (props: VirtualProps, items: Ref<readonly T[]>) {
}

const computedItems = computed(() => {
return items.value.slice(first.value, last.value).map((item, index) => ({
raw: item,
index: index + first.value,
key: (isObject(item) && 'value' in item) ? item.value : index + first.value,
}))
return items.value.slice(first.value, last.value).map((item, index) => {
const _index = index + first.value
return {
raw: item,
index: _index,
key: getPropertyFromItem(item, props.itemKey, _index),
}
})
})

watch(items, () => {
Expand Down

0 comments on commit fabc511

Please # to comment.