From 89f530143b2fdb3eea617009acd82550aba84e73 Mon Sep 17 00:00:00 2001 From: Kael Date: Wed, 18 Jan 2023 22:25:36 +1100 Subject: [PATCH 1/5] feat: support multiple filter result highlighting, add to VDataTable --- .../VAutocomplete/VAutocomplete.tsx | 21 +---- .../src/components/VCombobox/VCombobox.tsx | 21 +---- .../src/composables/{filter.ts => filter.tsx} | 84 ++++++++++++++----- .../src/labs/VDataTable/VDataTable.sass | 3 + .../src/labs/VDataTable/VDataTable.tsx | 3 +- .../src/labs/VDataTable/VDataTableRow.tsx | 6 +- .../src/labs/VDataTable/VDataTableRows.tsx | 2 + .../src/labs/VDataTable/VDataTableVirtual.tsx | 7 +- 8 files changed, 80 insertions(+), 67 deletions(-) rename packages/vuetify/src/composables/{filter.ts => filter.tsx} (60%) diff --git a/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx b/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx index 97ed6288b83..219e647e286 100644 --- a/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx +++ b/packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx @@ -12,7 +12,7 @@ import { VTextField } from '@/components/VTextField' // Composables import { forwardRefs } from '@/composables/forwardRefs' -import { makeFilterProps, useFilter } from '@/composables/filter' +import { highlightResult, makeFilterProps, useFilter } from '@/composables/filter' import { makeTransitionProps } from '@/composables/transition' import { useForm } from '@/composables/form' import { useItems } from '@/composables/items' @@ -25,28 +25,11 @@ import { genericComponent, omit, useRender, wrapInArray } from '@/util' import { filterVTextFieldProps, makeVTextFieldProps } from '../VTextField/VTextField' // Types -import type { FilterMatch } from '@/composables/filter' import type { InternalItem } from '@/composables/items' import type { MakeSlots, SlotsToProps } from '@/util' import type { VFieldSlots } from '@/components/VField/VField' import type { VInputSlots } from '@/components/VInput/VInput' -function highlightResult (text: string, matches: FilterMatch | undefined, length: number) { - if (matches == null) return text - - if (Array.isArray(matches)) throw new Error('Multiple matches is not implemented') - - return typeof matches === 'number' && ~matches - ? ( - <> - { text.substr(0, matches) } - { text.substr(matches, length) } - { text.substr(matches + length) } - - ) - : text -} - type Primitive = string | number | boolean | symbol type Val = T extends Primitive @@ -328,7 +311,7 @@ export const VAutocomplete = genericComponent { return isPristine.value ? item.title - : highlightResult(item.title, getMatches(item)?.title, search.value?.length ?? 0) + : highlightResult('v-autocomplete', item.title, getMatches(item)?.title) }, }} diff --git a/packages/vuetify/src/components/VCombobox/VCombobox.tsx b/packages/vuetify/src/components/VCombobox/VCombobox.tsx index 37e83e7f09f..e6a2dc85fa0 100644 --- a/packages/vuetify/src/components/VCombobox/VCombobox.tsx +++ b/packages/vuetify/src/components/VCombobox/VCombobox.tsx @@ -12,7 +12,7 @@ import { VTextField } from '@/components/VTextField' // Composables import { forwardRefs } from '@/composables/forwardRefs' -import { makeFilterProps, useFilter } from '@/composables/filter' +import { highlightResult, makeFilterProps, useFilter } from '@/composables/filter' import { makeTransitionProps } from '@/composables/transition' import { transformItem, useItems } from '@/composables/items' import { useForm } from '@/composables/form' @@ -28,27 +28,10 @@ import { filterVTextFieldProps, makeVTextFieldProps } from '../VTextField/VTextF // Types import type { PropType } from 'vue' import type { MakeSlots, SlotsToProps } from '@/util' -import type { FilterMatch } from '@/composables/filter' import type { InternalItem } from '@/composables/items' import type { VFieldSlots } from '@/components/VField/VField' import type { VInputSlots } from '@/components/VInput/VInput' -function highlightResult (text: string, matches: FilterMatch | undefined, length: number) { - if (matches == null) return text - - if (Array.isArray(matches)) throw new Error('Multiple matches is not implemented') - - return typeof matches === 'number' && ~matches - ? ( - <> - { text.substr(0, matches) } - { text.substr(matches, length) } - { text.substr(matches + length) } - - ) - : text -} - type Primitive = string | number | boolean | symbol type Val = string | (T extends Primitive @@ -407,7 +390,7 @@ export const VCombobox = genericComponent { return isPristine.value ? item.title - : highlightResult(item.title, getMatches(item)?.title, search.value?.length ?? 0) + : highlightResult('v-combobox', item.title, getMatches(item)?.title) }, }} diff --git a/packages/vuetify/src/composables/filter.ts b/packages/vuetify/src/composables/filter.tsx similarity index 60% rename from packages/vuetify/src/composables/filter.ts rename to packages/vuetify/src/composables/filter.tsx index 030fb892ce0..c33abde5fb1 100644 --- a/packages/vuetify/src/composables/filter.ts +++ b/packages/vuetify/src/composables/filter.tsx @@ -3,7 +3,7 @@ // Utilities import { getPropertyFromItem, propsFactory, wrapInArray } from '@/util' -import { computed, ref, unref, watchEffect } from 'vue' +import { computed, ref, toRaw, unref, watchEffect } from 'vue' // Types import type { PropType, Ref } from 'vue' @@ -11,12 +11,15 @@ import type { MaybeRef } from '@/util' import type { InternalItem } from './items' /** - * - match without highlight - * - single match (index), length already known - * - single match (start, end) - * - multiple matches (start, end), probably shouldn't overlap + * - boolean: match without highlight + * - number: single match (index), length already known + * - []: single match (start, end) + * - [][]: multiple matches (start, end), shouldn't overlap */ -export type FilterMatch = boolean | number | [number, number] | [number, number][] +export type FilterMatchArraySingle = readonly [number, number] +export type FilterMatchArrayMultiple = readonly FilterMatchArraySingle[] +export type FilterMatchArray = FilterMatchArraySingle | FilterMatchArrayMultiple +export type FilterMatch = boolean | number | FilterMatchArray export type FilterFunction = (value: string, query: string, item?: any) => FilterMatch export type FilterKeyFunctions = Record export type FilterKeys = string | string[] @@ -34,7 +37,25 @@ export interface FilterProps { export const defaultFilter: FilterFunction = (value, query, item) => { if (value == null || query == null) return -1 - return value.toString().toLocaleLowerCase().indexOf(query.toString().toLocaleLowerCase()) + value = value.toString().toLocaleLowerCase() + query = query.toString().toLocaleLowerCase() + + const result = [] + let idx = value.indexOf(query) + while (~idx) { + result.push([idx, idx + query.length] as const) + + idx = value.indexOf(query, idx + query.length) + } + + return result.length ? result : -1 +} + +function normaliseMatch (match: FilterMatch, query: string): FilterMatchArrayMultiple | undefined { + if (match == null || typeof match === 'boolean' || match === -1) return + if (typeof match === 'number') return [[match, query.length]] + if (Array.isArray(match[0])) return match as FilterMatchArrayMultiple + return [match] as FilterMatchArrayMultiple } export const makeFilterProps = propsFactory({ @@ -59,7 +80,7 @@ export function filterItems ( noFilter?: boolean }, ) { - const array: { index: number, matches: Record }[] = [] + const array: { index: number, matches: Record }[] = [] // always ensure we fall back to a functioning filter const filter = options?.default ?? defaultFilter const keys = options?.filterKeys ? wrapInArray(options.filterKeys) : false @@ -70,8 +91,8 @@ export function filterItems ( loop: for (let i = 0; i < items.length; i++) { const item = items[i].raw - const customMatches: Record = {} - const defaultMatches: Record = {} + const customMatches: Record = {} + const defaultMatches: Record = {} let match: FilterMatch = -1 if (query && !options?.noFilter) { @@ -87,8 +108,8 @@ export function filterItems ( : filter(value, query, item) if (match !== -1 && match !== false) { - if (keyFilter) customMatches[key] = match - else defaultMatches[key] = match + if (keyFilter) customMatches[key] = normaliseMatch(match, query) + else defaultMatches[key] = normaliseMatch(match, query) } else if (options?.filterMode === 'every') { continue loop } @@ -96,7 +117,7 @@ export function filterItems ( } else { match = filter(item, query, item) if (match !== -1 && match !== false) { - defaultMatches.title = match + defaultMatches.title = normaliseMatch(match, query) } } @@ -137,14 +158,14 @@ export function useFilter ( ) ? '' : String(query.value)) const filteredItems: Ref = ref([]) - const filteredMatches: Ref>> = ref(new Map()) + const filteredMatches = ref(new Map>()) watchEffect(() => { - filteredItems.value = [] - filteredMatches.value = new Map() + const i: T[] = [] + const m = new Map>() - const transformedItems = unref(items) - const results = filterItems( + const transformedItems = toRaw(unref(items)) + filterItems( transformedItems, strQuery.value, { @@ -154,13 +175,14 @@ export function useFilter ( filterMode: props.filterMode, noFilter: props.noFilter, }, - ) - - results.forEach(({ index, matches }) => { + ).forEach(({ index, matches }) => { const item = transformedItems[index] - filteredItems.value.push(item) - filteredMatches.value.set(item.value, matches) + i.push(item) + m.set(item.value, matches) }) + + filteredItems.value = i + filteredMatches.value = m }) function getMatches (item: T) { @@ -169,3 +191,19 @@ export function useFilter ( return { filteredItems, filteredMatches, getMatches } } + +export function highlightResult (name: string, text: string, matches: FilterMatchArrayMultiple | undefined) { + if (matches == null || !matches.length) return text + + return matches.map((match, i) => { + const start = i === 0 ? 0 : matches[i - 1][1] + const result = [ + { text.slice(start, match[0]) }, + { text.slice(match[0], match[1]) }, + ] + if (i === matches.length - 1) { + result.push({ text.slice(match[1]) }) + } + return <>{ result } + }) +} diff --git a/packages/vuetify/src/labs/VDataTable/VDataTable.sass b/packages/vuetify/src/labs/VDataTable/VDataTable.sass index a76803daa17..8b0953e4d45 100644 --- a/packages/vuetify/src/labs/VDataTable/VDataTable.sass +++ b/packages/vuetify/src/labs/VDataTable/VDataTable.sass @@ -104,3 +104,6 @@ .v-data-table-rows-no-data text-align: center + +.v-data-table__mask + background: rgb(var(--v-theme-on-surface-variant)) diff --git a/packages/vuetify/src/labs/VDataTable/VDataTable.tsx b/packages/vuetify/src/labs/VDataTable/VDataTable.tsx index 3eb1fc7f281..7d920fd67b1 100644 --- a/packages/vuetify/src/labs/VDataTable/VDataTable.tsx +++ b/packages/vuetify/src/labs/VDataTable/VDataTable.tsx @@ -78,7 +78,7 @@ export const VDataTable = defineComponent({ const { items } = useDataTableItems(props, columns) - const { filteredItems } = useFilter(props, items, toRef(props, 'search')) + const { filteredItems, getMatches } = useFilter(props, items, toRef(props, 'search')) const { sortBy } = createSort(props) const { sortByWithGroups, opened, extractRows } = createGroupBy(props, groupBy, sortBy) @@ -142,6 +142,7 @@ export const VDataTable = defineComponent({ { slots.body ? slots.body() : ( emit('click:row', event, value) } v-slots={ slots } /> diff --git a/packages/vuetify/src/labs/VDataTable/VDataTableRow.tsx b/packages/vuetify/src/labs/VDataTable/VDataTableRow.tsx index 1a460c17d12..e994d157733 100644 --- a/packages/vuetify/src/labs/VDataTable/VDataTableRow.tsx +++ b/packages/vuetify/src/labs/VDataTable/VDataTableRow.tsx @@ -1,12 +1,13 @@ // Components import { VBtn } from '@/components/VBtn' import { VCheckboxBtn } from '@/components/VCheckbox' +import { VDataTableColumn } from './VDataTableColumn' // Composables +import { highlightResult } from '@/composables/filter' import { useExpanded } from './composables/expand' import { useHeaders } from './composables/headers' import { useSelection } from './composables/select' -import { VDataTableColumn } from './VDataTableColumn' // Utilities import { defineComponent, useRender } from '@/util' @@ -20,6 +21,7 @@ export const VDataTableRow = defineComponent({ props: { item: Object as PropType, + getMatches: Function, }, setup (props, { slots }) { @@ -84,7 +86,7 @@ export const VDataTableRow = defineComponent({ ) } - return item.columns[column.key] + return highlightResult('v-data-table', String(item.columns[column.key]), props.getMatches!(item)?.[column.key]) }, }} diff --git a/packages/vuetify/src/labs/VDataTable/VDataTableRows.tsx b/packages/vuetify/src/labs/VDataTable/VDataTableRows.tsx index 81c0fdce650..58944882fc6 100644 --- a/packages/vuetify/src/labs/VDataTable/VDataTableRows.tsx +++ b/packages/vuetify/src/labs/VDataTable/VDataTableRows.tsx @@ -35,6 +35,7 @@ export const VDataTableRows = defineComponent({ default: '$vuetify.noDataText', }, rowHeight: Number, + getMatches: Function, }, emits: { @@ -110,6 +111,7 @@ export const VDataTableRows = defineComponent({ emit('click:row', event, { item }) } } item={ item } + getMatches={ props.getMatches } v-slots={ slots } /> ) } diff --git a/packages/vuetify/src/labs/VDataTable/VDataTableVirtual.tsx b/packages/vuetify/src/labs/VDataTable/VDataTableVirtual.tsx index 1b1f286f05a..dfd74381c4b 100644 --- a/packages/vuetify/src/labs/VDataTable/VDataTableVirtual.tsx +++ b/packages/vuetify/src/labs/VDataTable/VDataTableVirtual.tsx @@ -60,7 +60,7 @@ export const VDataTableVirtual = defineComponent({ }) const { items } = useDataTableItems(props, columns) - const { filteredItems } = useFilter(props, items, toRef(props, 'search')) + const { filteredItems, getMatches } = useFilter(props, items, toRef(props, 'search')) const { sortBy } = createSort(props) const { sortByWithGroups, opened, extractRows } = createGroupBy(props, groupBy, sortBy) @@ -133,11 +133,12 @@ export const VDataTableVirtual = defineComponent({ - + emit('click:row', event, value) } v-slots={ slots } /> From 7dbfe6b37b3aceb53bac6ca25ab74074df2bd94f Mon Sep 17 00:00:00 2001 From: Kael Date: Thu, 6 Mar 2025 21:55:21 +1100 Subject: [PATCH 2/5] feat(VDataTable): highlight filter matches --- .../vuetify/src/components/VDataTable/VDataTable.sass | 3 +++ packages/vuetify/src/components/VDataTable/VDataTable.tsx | 3 ++- .../vuetify/src/components/VDataTable/VDataTableRow.tsx | 8 +++++++- .../vuetify/src/components/VDataTable/VDataTableRows.tsx | 2 ++ .../src/components/VDataTable/VDataTableVirtual.tsx | 3 ++- packages/vuetify/src/composables/filter.tsx | 2 +- 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/vuetify/src/components/VDataTable/VDataTable.sass b/packages/vuetify/src/components/VDataTable/VDataTable.sass index 7f9b6d87b98..64a9d0a7d4a 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTable.sass +++ b/packages/vuetify/src/components/VDataTable/VDataTable.sass @@ -164,3 +164,6 @@ &-active color: $data-table-header-mobile-chip-icon-color-active + +.v-data-table__mask + background: rgb(var(--v-theme-on-surface-variant)) diff --git a/packages/vuetify/src/components/VDataTable/VDataTable.tsx b/packages/vuetify/src/components/VDataTable/VDataTable.tsx index b7e3b5763ac..b3db17dfff0 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTable.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTable.tsx @@ -147,7 +147,7 @@ export const VDataTable = genericComponent( const { items } = useDataTableItems(props, columns) const search = toRef(props, 'search') - const { filteredItems } = useFilter(props, items, search, { + const { filteredItems, getMatches } = useFilter(props, items, search, { transform: item => item.columns, customKeyFilter: filterFunctions, }) @@ -262,6 +262,7 @@ export const VDataTable = genericComponent( { ...attrs } { ...dataTableRowsProps } items={ paginatedItems.value } + getMatches={ getMatches } v-slots={ slots } /> )} diff --git a/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx b/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx index f6a9f333509..b01d7660007 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx @@ -9,6 +9,7 @@ import { useHeaders } from './composables/headers' import { useSelection } from './composables/select' import { useSort } from './composables/sort' import { makeDisplayProps, useDisplay } from '@/composables/display' +import { highlightResult } from '@/composables/filter' // Utilities import { toDisplayString, withModifiers } from 'vue' @@ -34,6 +35,7 @@ export const makeVDataTableRowProps = propsFactory({ index: Number, item: Object as PropType, cellProps: [Object, Function] as PropType>, + getMatches: Function, onClick: EventProp<[MouseEvent]>(), onContextmenu: EventProp<[MouseEvent]>(), onDblclick: EventProp<[MouseEvent]>(), @@ -159,7 +161,11 @@ export const VDataTableRow = genericComponent( ) } - const displayValue = toDisplayString(slotProps.value) + const displayValue = highlightResult( + 'v-data-table', + toDisplayString(slotProps.value), + props.getMatches!(item)?.[column.key!] + ) return !mobile.value ? displayValue : ( <> diff --git a/packages/vuetify/src/components/VDataTable/VDataTableRows.tsx b/packages/vuetify/src/components/VDataTable/VDataTableRows.tsx index cd61f72b968..dd03c9cffef 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableRows.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableRows.tsx @@ -47,6 +47,7 @@ export const makeVDataTableRowsProps = propsFactory({ }, rowProps: [Object, Function] as PropType>, cellProps: [Object, Function] as PropType>, + getMatches: Function, ...makeDisplayProps(), }, 'VDataTableRows') @@ -164,6 +165,7 @@ export const VDataTableRows = genericComponent( { slots.item ? slots.item(itemSlotProps) : ( )} diff --git a/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx b/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx index 4cf93b331f7..a5c0c949315 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx @@ -104,7 +104,7 @@ export const VDataTableVirtual = genericComponent item.columns, customKeyFilter: filterFunctions, }) @@ -231,6 +231,7 @@ export const VDataTableVirtual = genericComponent {{ ...slots, diff --git a/packages/vuetify/src/composables/filter.tsx b/packages/vuetify/src/composables/filter.tsx index ad568669f8d..32193fc3a80 100644 --- a/packages/vuetify/src/composables/filter.tsx +++ b/packages/vuetify/src/composables/filter.tsx @@ -160,7 +160,7 @@ export function useFilter ( customKeyFilter?: MaybeRef } ) { - const filteredItems= shallowRef([]) + const filteredItems = shallowRef([]) const filteredMatches = shallowRef(new Map>()) const transformedItems = computed(() => ( options?.transform From 93bc24d0bf11f398c23b5829ae32963ebca54ad5 Mon Sep 17 00:00:00 2001 From: Kael Date: Thu, 6 Mar 2025 22:05:44 +1100 Subject: [PATCH 3/5] fix tests --- .../vuetify/src/composables/__tests__/filter.spec.ts | 10 +++++----- packages/vuetify/src/composables/filter.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vuetify/src/composables/__tests__/filter.spec.ts b/packages/vuetify/src/composables/__tests__/filter.spec.ts index 751fd2e52b7..680ed76fd49 100644 --- a/packages/vuetify/src/composables/__tests__/filter.spec.ts +++ b/packages/vuetify/src/composables/__tests__/filter.spec.ts @@ -20,15 +20,15 @@ describe('filter', () => { it.each([ [null, null, -1], ['foo', null, -1], - ['foo', 'foo', 0], - ['foo', 'f', 0], + ['foo', 'foo', [[0, 3]]], + ['foo', 'f', [[0, 1]]], [null, 'foo', -1], ['foo', 'bar', -1], - [1, '1', 0], - ['1', 1, 0], + [1, '1', [[0, 1]]], + ['1', 1, [[0, 1]]], ])('should compare %s to %s and return a match result', (text, query, expected) => { // @ts-expect-error - expect(defaultFilter(text, query)).toBe(expected) + expect(defaultFilter(text, query)).toStrictEqual(expected) }) }) diff --git a/packages/vuetify/src/composables/filter.tsx b/packages/vuetify/src/composables/filter.tsx index ad568669f8d..32193fc3a80 100644 --- a/packages/vuetify/src/composables/filter.tsx +++ b/packages/vuetify/src/composables/filter.tsx @@ -160,7 +160,7 @@ export function useFilter ( customKeyFilter?: MaybeRef } ) { - const filteredItems= shallowRef([]) + const filteredItems = shallowRef([]) const filteredMatches = shallowRef(new Map>()) const transformedItems = computed(() => ( options?.transform From 40c17903e912dd1c4294dbf47d518bcc41aa3e56 Mon Sep 17 00:00:00 2001 From: Kael Date: Fri, 7 Mar 2025 18:12:02 +1100 Subject: [PATCH 4/5] fix: ignore unkeyed items --- packages/vuetify/src/composables/filter.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vuetify/src/composables/filter.tsx b/packages/vuetify/src/composables/filter.tsx index 32193fc3a80..ff62b05896c 100644 --- a/packages/vuetify/src/composables/filter.tsx +++ b/packages/vuetify/src/composables/filter.tsx @@ -197,7 +197,9 @@ export function useFilter ( results.forEach(({ index, matches }) => { const item = originalItems[index] _filteredItems.push(item) - _filteredMatches.set(item.value, matches) + if (item.value !== undefined) { + _filteredMatches.set(item.value, matches) + } }) filteredItems.value = _filteredItems filteredMatches.value = _filteredMatches From 0538333aa624031f33a9225c6a8878660c80fd61 Mon Sep 17 00:00:00 2001 From: Kael Date: Fri, 7 Mar 2025 18:16:49 +1100 Subject: [PATCH 5/5] fix missing getMatches --- .../src/components/VDataTable/VDataTableRow.tsx | 12 +++++++----- .../src/components/VDataTable/VDataTableVirtual.tsx | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx b/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx index 059865cf7d1..d56ce6fb4f1 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableRow.tsx @@ -164,11 +164,13 @@ export const VDataTableRow = genericComponent( ) } - const displayValue = highlightResult( - 'v-data-table', - toDisplayString(slotProps.value), - props.getMatches!(item)?.[column.key!] - ) + const displayValue = props.getMatches + ? highlightResult( + 'v-data-table', + toDisplayString(slotProps.value), + props.getMatches(item)?.[column.key!] + ) + : toDisplayString(slotProps.value) return !mobile.value ? displayValue : ( <> diff --git a/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx b/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx index a5c0c949315..9fd4f83bf33 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx @@ -248,6 +248,7 @@ export const VDataTableVirtual = genericComponent )