Skip to content

Commit d668d6f

Browse files
authored
Merge branch 'master' into fix/v-menu-overlay-background-with-v-input
2 parents 8de97da + 0960aa6 commit d668d6f

File tree

9 files changed

+108
-57
lines changed

9 files changed

+108
-57
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<v-app>
3+
<v-container>
4+
<v-autocomplete :items="items">
5+
<template v-slot:item="{ props }">
6+
<v-list-item v-bind="props"></v-list-item>
7+
</template>
8+
</v-autocomplete>
9+
</v-container>
10+
</v-app>
11+
</template>
12+
13+
<script setup>
14+
const items = [
15+
...Array.from({ length: 50 }, (_, i) => ({
16+
value: (i + 3).toString().padStart(19, '0'),
17+
title: `Mock ${i + 1}`,
18+
})),
19+
]
20+
</script>
21+
22+
<script>
23+
export default {
24+
data: () => ({
25+
items: Array.from({ length: 50 }, (_, i) => ({
26+
value: (i + 3).toString().padStart(19, '0'),
27+
title: `Mock ${i + 1}`,
28+
})),
29+
}),
30+
}
31+
</script>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<template>
2+
<v-app>
3+
<v-container>
4+
<v-text-field v-model="msg" :focused="true" @update:focused=""></v-text-field>
5+
</v-container>
6+
</v-app>
7+
</template>
8+
9+
<script setup>
10+
import { ref } from 'vue'
11+
12+
const msg = ref('Hello World!')
13+
</script>

packages/docs/src/pages/en/components/autocompletes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ With the power of slots, you can customize the visual output of the select. In t
9494

9595
<ExamplesExample file="v-autocomplete/slot-item-and-selection" />
9696

97+
When customizing v-autocomplete items with the #item slot, make sure to forward the slot props using v-bind="props".
98+
This is required for virtual scrolling to work properly — without it, only part of your items may be displayed.
99+
100+
<ExamplesExample file="v-autocomplete/slot-item-and-vbind-props" />
101+
97102
### Misc
98103

99104
<!--

packages/docs/src/pages/en/components/text-fields.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ The **variant** prop provides an easy way to customize the style of your text fi
222222

223223
<ExamplesExample file="v-text-field/prop-variant" />
224224

225+
#### Focused
226+
227+
The **focused** prop that sets the initial focus state of the component. It is a model prop, which handles 2 way binding with `focused` and `@update:focused`. This means its value sets the initial state but will be updated internally by focus/blur events.
228+
If you want to override this behavior (e.g., keep the input always focused), you can bind an empty `@update:focused` handler.
229+
230+
<ExamplesExample file="v-text-field/prop-focused" />
231+
225232
### Events
226233

227234
#### Icon events

packages/vuetify/src/components/VSlideGroup/VSlideGroup.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ export const VSlideGroup = genericComponent<new <T>(
344344
isSelected: group.isSelected,
345345
}))
346346

347+
const hasOverflowOrScroll = computed(() => isOverflowing.value || Math.abs(scrollOffset.value) > 0)
348+
347349
const hasAffixes = computed(() => {
348350
switch (props.showArrows) {
349351
// Always show arrows on desktop & mobile
@@ -354,20 +356,20 @@ export const VSlideGroup = genericComponent<new <T>(
354356

355357
// Show arrows on mobile when overflowing.
356358
// This matches the default 2.2 behavior
357-
case true: return isOverflowing.value || Math.abs(scrollOffset.value) > 0
359+
case true: return hasOverflowOrScroll.value
358360

359361
// Always show on mobile
360362
case 'mobile': return (
361363
mobile.value ||
362-
(isOverflowing.value || Math.abs(scrollOffset.value) > 0)
364+
hasOverflowOrScroll.value
363365
)
364366

365367
// https://material.io/components/tabs#scrollable-tabs
366368
// Always show arrows when
367369
// overflowed on desktop
368370
default: return (
369371
!mobile.value &&
370-
(isOverflowing.value || Math.abs(scrollOffset.value) > 0)
372+
hasOverflowOrScroll.value
371373
)
372374
}
373375
})
@@ -378,7 +380,7 @@ export const VSlideGroup = genericComponent<new <T>(
378380
})
379381

380382
const hasNext = computed(() => {
381-
if (!containerRef.value) return false
383+
if (!containerRef.value || !hasOverflowOrScroll.value) return false
382384

383385
const scrollSize = getScrollSize(isHorizontal.value, containerRef.el)
384386
const clientSize = getClientSize(isHorizontal.value, containerRef.el)

packages/vuetify/src/composables/__tests__/filter.spec.ts

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,10 @@ describe('filter', () => {
6464
value: (s: string) => s === '1',
6565
}
6666
const items = [
67-
{
68-
title: 'foo',
69-
subtitle: 'bar',
70-
value: '1',
71-
custom: '1',
72-
},
73-
{
74-
title: 'fizz',
75-
subtitle: 'buzz',
76-
value: '1',
77-
custom: 'bar',
78-
},
79-
{
80-
title: 'foobar',
81-
subtitle: 'fizzbuzz',
82-
value: '2',
83-
custom: 'bar',
84-
},
85-
{
86-
title: 'buzz',
87-
subtitle: 'buzz',
88-
value: '1',
89-
custom: 'buzz',
90-
},
67+
{ title: 'foo', subtitle: 'bar', value: '1', custom: '1' },
68+
{ title: 'fizz', subtitle: 'buzz', value: '1', custom: 'bar' },
69+
{ title: 'foobar', subtitle: 'fizzbuzz', value: '2', custom: 'bar' },
70+
{ title: 'buzz', subtitle: 'buzz', value: '1', custom: 'buzz' },
9171
] as any
9272
const filterKeys = ['title', 'value', 'subtitle', 'custom']
9373

@@ -122,26 +102,10 @@ describe('filter', () => {
122102
value: (s: string) => s === '1',
123103
}
124104
const items = [
125-
{
126-
title: 'foo',
127-
subtitle: 'bar',
128-
value: '1',
129-
},
130-
{
131-
title: 'fizz',
132-
subtitle: 'buzz',
133-
value: '1',
134-
},
135-
{
136-
title: 'foobar',
137-
subtitle: 'fizzbuzz',
138-
value: '2',
139-
},
140-
{
141-
title: 'buzz',
142-
subtitle: 'buzz',
143-
value: '2',
144-
},
105+
{ title: 'foo', subtitle: 'bar', value: '1' },
106+
{ title: 'fizz', subtitle: 'buzz', value: '1' },
107+
{ title: 'foobar', subtitle: 'fizzbuzz', value: '2' },
108+
{ title: 'buzz', subtitle: 'buzz', value: '2' },
145109
] as any
146110
const filterKeys = ['title', 'value']
147111

@@ -161,14 +125,33 @@ describe('filter', () => {
161125
filterKeys,
162126
customKeyFilter,
163127
filterMode: 'intersection',
164-
})).toHaveLength(0)
128+
})).toHaveLength(2)
165129

166130
expect(filterItems(items, '', {
167131
filterKeys,
168132
customKeyFilter,
169133
filterMode: 'every',
170134
})).toHaveLength(2)
171135
})
136+
137+
// https://github.com/vuetifyjs/vuetify/pull/21876
138+
it('should return filtered rows when all columns have filters', () => {
139+
const customKeyFilter = {
140+
title: (s: string) => s.length < 5,
141+
subtitle: (s: string) => s.startsWith('b'),
142+
value: (s: any) => Number(s) > 0,
143+
}
144+
const items = [
145+
{ title: 'foo', subtitle: 'bar', value: 1 },
146+
{ title: 'fizz', subtitle: 'buzz', value: 1 },
147+
{ title: 'foobar', subtitle: 'fizzbuzz', value: 2 },
148+
] as any
149+
150+
expect(filterItems(items, '', {
151+
customKeyFilter,
152+
filterMode: 'intersection',
153+
})).toHaveLength(2)
154+
})
172155
})
173156

174157
describe('useFilter', () => {

packages/vuetify/src/composables/filter.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,15 @@ export function filterItems (
102102
let match: FilterMatch = -1
103103

104104
if ((query || customFiltersLength > 0) && !options?.noFilter) {
105+
let hasOnlyCustomFilters = false
106+
105107
if (typeof item === 'object') {
106108
if (item.type === 'divider' || item.type === 'subheader') {
107109
continue
108110
}
109111

110112
const filterKeys = keys || Object.keys(transformed)
113+
hasOnlyCustomFilters = filterKeys.length === customFiltersLength
111114

112115
for (const key of filterKeys) {
113116
const value = getPropertyFromItem(transformed, key)
@@ -146,7 +149,7 @@ export function filterItems (
146149
options?.filterMode === 'intersection' &&
147150
(
148151
customMatchesLength !== customFiltersLength ||
149-
!defaultMatchesLength
152+
(!defaultMatchesLength && customFiltersLength > 0 && !hasOnlyCustomFilters)
150153
)
151154
) continue
152155
}

packages/vuetify/src/composables/nested/nested.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export const useNested = (props: NestedProps) => {
206206
const path: unknown[] = []
207207
let parent: unknown = toRaw(id)
208208

209-
while (parent != null) {
209+
while (parent !== undefined) {
210210
path.unshift(parent)
211211
parent = parents.value.get(parent)
212212
}
@@ -358,7 +358,10 @@ export const useNestedItem = (id: MaybeRefOrGetter<unknown>, isDisabled: MaybeRe
358358
const parent = inject(VNestedSymbol, emptyNested)
359359

360360
const uidSymbol = Symbol('nested item')
361-
const computedId = computed(() => toRaw(toValue(id)) ?? uidSymbol)
361+
const computedId = computed(() => {
362+
const idValue = toRaw(toValue(id))
363+
return idValue !== undefined ? idValue : uidSymbol
364+
})
362365

363366
const item = {
364367
...parent,

packages/vuetify/src/labs/VDateInput/VDateInput.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import { useProxiedModel } from '@/composables/proxiedModel'
1616

1717
// Utilities
1818
import { computed, ref, shallowRef, watch } from 'vue'
19-
import { genericComponent, omit, propsFactory, useRender, wrapInArray } from '@/util'
19+
import { genericComponent, omit, pick, propsFactory, useRender, wrapInArray } from '@/util'
2020

2121
// Types
2222
import type { PropType } from 'vue'
23+
import type { VDatePickerSlots } from '@/components/VDatePicker/VDatePicker'
2324
import type { StrategyProps } from '@/components/VOverlay/locationStrategies'
2425
import type { VTextFieldSlots } from '@/components/VTextField/VTextField'
2526
import type { GenericProps } from '@/util'
@@ -31,10 +32,11 @@ export type VDateInputActionsSlot = {
3132
isPristine: boolean
3233
}
3334

34-
export type VDateInputSlots = Omit<VTextFieldSlots, 'default'> & {
35-
actions: VDateInputActionsSlot
36-
default: never
37-
}
35+
export type VDateInputSlots = Omit<VTextFieldSlots, 'default'> &
36+
Pick<VDatePickerSlots, 'title' | 'header' | 'day' | 'month' | 'year'> & {
37+
actions: VDateInputActionsSlot
38+
default: never
39+
}
3840

3941
export const makeVDateInputProps = propsFactory({
4042
displayFormat: {
@@ -245,6 +247,7 @@ export const VDateInput = genericComponent<new <
245247
useRender(() => {
246248
const confirmEditProps = VConfirmEdit.filterProps(props)
247249
const datePickerProps = VDatePicker.filterProps(omit(props, ['active', 'location', 'rounded']))
250+
const datePickerSlots = pick(slots, ['title', 'header', 'day', 'month', 'year'])
248251
const textFieldProps = VTextField.filterProps(omit(props, ['placeholder']))
249252

250253
return (
@@ -312,6 +315,7 @@ export const VDateInput = genericComponent<new <
312315
onMousedown={ (e: MouseEvent) => e.preventDefault() }
313316
>
314317
{{
318+
...datePickerSlots,
315319
actions: !props.hideActions ? () => slots.actions?.({ save, cancel, isPristine }) ?? actions() : undefined,
316320
}}
317321
</VDatePicker>

0 commit comments

Comments
 (0)