Skip to content

Commit 32e0f5c

Browse files
committed
fix(nested): don't toggle disabled children
fixes #20386
1 parent fac2f99 commit 32e0f5c

File tree

4 files changed

+35
-14
lines changed

4 files changed

+35
-14
lines changed

packages/vuetify/src/components/VList/VListGroup.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const makeVListGroupProps = propsFactory({
3939
type: IconValue,
4040
default: '$collapse',
4141
},
42+
disabled: Boolean,
4243
expandIcon: {
4344
type: IconValue,
4445
default: '$expand',
@@ -60,7 +61,7 @@ export const VListGroup = genericComponent<VListGroupSlots>()({
6061
props: makeVListGroupProps(),
6162

6263
setup (props, { slots }) {
63-
const { isOpen, open, id: _id } = useNestedItem(toRef(props, 'value'), true)
64+
const { isOpen, open, id: _id } = useNestedItem(toRef(props, 'value'), toRef(props, 'disabled'), true)
6465
const id = computed(() => `v-list-group--id-${String(_id.value)}`)
6566
const list = useList()
6667
const { isBooted } = useSsrBoot()

packages/vuetify/src/components/VList/VListItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { genOverlays, makeVariantProps, useVariant } from '@/composables/variant
2727
import { Ripple } from '@/directives/ripple'
2828

2929
// Utilities
30-
import { computed, onBeforeMount, watch } from 'vue'
30+
import { computed, onBeforeMount, toRef, watch } from 'vue'
3131
import { deprecate, EventProp, genericComponent, propsFactory, useRender } from '@/util'
3232

3333
// Types
@@ -128,7 +128,7 @@ export const VListItem = genericComponent<VListItemSlots>()({
128128
parent,
129129
openOnSelect,
130130
id: uid,
131-
} = useNestedItem(id, false)
131+
} = useNestedItem(id, toRef(props, 'disabled'), false)
132132
const list = useList()
133133
const isActive = computed(() =>
134134
props.active !== false &&

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,15 @@ type NestedProvide = {
7373
isGroupActivator?: boolean
7474
root: {
7575
children: Ref<Map<unknown, unknown[]>>
76+
disabled: Ref<Set<unknown>>
7677
parents: Ref<Map<unknown, unknown>>
7778
activatable: Ref<boolean>
7879
selectable: Ref<boolean>
7980
opened: Ref<Set<unknown>>
8081
activated: Ref<Set<unknown>>
8182
selected: Ref<Map<unknown, 'on' | 'off' | 'indeterminate'>>
8283
selectedValues: Ref<unknown[]>
83-
register: (id: unknown, parentId: unknown, isGroup?: boolean) => void
84+
register: (id: unknown, parentId: unknown, isDisabled: boolean, isGroup?: boolean) => void
8485
unregister: (id: unknown) => void
8586
open: (id: unknown, value: boolean, event?: Event) => void
8687
activate: (id: unknown, value: boolean, event?: Event) => void
@@ -99,6 +100,7 @@ export const emptyNested: NestedProvide = {
99100
unregister: () => null,
100101
parents: ref(new Map()),
101102
children: ref(new Map()),
103+
disabled: ref(new Set()),
102104
open: () => null,
103105
openOnSelect: () => null,
104106
activate: () => null,
@@ -128,6 +130,7 @@ export const makeNestedProps = propsFactory({
128130
export const useNested = (props: NestedProps) => {
129131
let isUnmounted = false
130132
const children = ref(new Map<unknown, unknown[]>())
133+
const disabled = ref(new Set<unknown>())
131134
const parents = ref(new Map<unknown, unknown>())
132135

133136
const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(v), v => [...v.values()])
@@ -181,7 +184,7 @@ export const useNested = (props: NestedProps) => {
181184
props,
182185
'selected',
183186
props.selected,
184-
v => selectStrategy.value.in(v, children.value, parents.value),
187+
v => selectStrategy.value.in(v, children.value, disabled.value, parents.value),
185188
v => selectStrategy.value.out(v, children.value, parents.value),
186189
)
187190

@@ -222,7 +225,7 @@ export const useNested = (props: NestedProps) => {
222225

223226
return arr
224227
}),
225-
register: (id, parentId, isGroup) => {
228+
register: (id, parentId, isDisabled, isGroup) => {
226229
if (nodeIds.has(id)) {
227230
const path = getPath(id).map(String).join(' -> ')
228231
const newPath = getPath(parentId).concat(id).map(String).join(' -> ')
@@ -234,6 +237,7 @@ export const useNested = (props: NestedProps) => {
234237

235238
parentId && id !== parentId && parents.value.set(id, parentId)
236239

240+
isDisabled && disabled.value.add(id)
237241
isGroup && children.value.set(id, [])
238242

239243
if (parentId != null) {
@@ -245,6 +249,7 @@ export const useNested = (props: NestedProps) => {
245249

246250
nodeIds.delete(id)
247251
children.value.delete(id)
252+
disabled.value.delete(id)
248253
const parent = parents.value.get(id)
249254
if (parent) {
250255
const list = children.value.get(parent) ?? []
@@ -286,6 +291,7 @@ export const useNested = (props: NestedProps) => {
286291
value,
287292
selected: new Map(selected.value),
288293
children: children.value,
294+
disabled: disabled.value,
289295
parents: parents.value,
290296
event,
291297
})
@@ -312,6 +318,7 @@ export const useNested = (props: NestedProps) => {
312318
newActivated && (activated.value = newActivated)
313319
},
314320
children,
321+
disabled,
315322
parents,
316323
getPath,
317324
},
@@ -322,7 +329,7 @@ export const useNested = (props: NestedProps) => {
322329
return nested.root
323330
}
324331

325-
export const useNestedItem = (id: Ref<unknown>, isGroup: boolean) => {
332+
export const useNestedItem = (id: Ref<unknown>, isDisabled: Ref<boolean>, isGroup: boolean) => {
326333
const parent = inject(VNestedSymbol, emptyNested)
327334

328335
const uidSymbol = Symbol(getUid())
@@ -345,7 +352,7 @@ export const useNestedItem = (id: Ref<unknown>, isGroup: boolean) => {
345352
}
346353

347354
onBeforeMount(() => {
348-
!parent.isGroupActivator && parent.root.register(computedId.value, parent.id.value, isGroup)
355+
!parent.isGroupActivator && parent.root.register(computedId.value, parent.id.value, isDisabled.value, isGroup)
349356
})
350357

351358
onBeforeUnmount(() => {

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ export type SelectStrategyFn = (data: {
77
value: boolean
88
selected: Map<unknown, 'on' | 'off' | 'indeterminate'>
99
children: Map<unknown, unknown[]>
10+
disabled: Set<unknown>
1011
parents: Map<unknown, unknown>
1112
event?: Event
1213
}) => Map<unknown, 'on' | 'off' | 'indeterminate'>
1314

1415
export type SelectStrategyTransformInFn = (
1516
v: readonly unknown[] | undefined,
1617
children: Map<unknown, unknown[]>,
18+
disabled: Set<unknown>,
1719
parents: Map<unknown, unknown>,
1820
) => Map<unknown, 'on' | 'off' | 'indeterminate'>
1921

@@ -138,7 +140,7 @@ export const leafSingleSelectStrategy = (mandatory?: boolean): SelectStrategy =>
138140

139141
export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
140142
const strategy: SelectStrategy = {
141-
select: ({ id, value, selected, children, parents }) => {
143+
select: ({ id, value, selected, children, disabled, parents }) => {
142144
id = toRaw(id)
143145
const original = new Map(selected)
144146

@@ -147,7 +149,9 @@ export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
147149
while (items.length) {
148150
const item = items.shift()!
149151

150-
selected.set(toRaw(item), value ? 'on' : 'off')
152+
if (!disabled.has(item)) {
153+
selected.set(toRaw(item), value ? 'on' : 'off')
154+
}
151155

152156
if (children.has(item)) {
153157
items.push(...children.get(item)!)
@@ -157,9 +161,17 @@ export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
157161
let parent = toRaw(parents.get(id))
158162

159163
while (parent) {
160-
const childrenIds = children.get(parent)!
161-
const everySelected = childrenIds.every(cid => selected.get(toRaw(cid)) === 'on')
162-
const noneSelected = childrenIds.every(cid => !selected.has(toRaw(cid)) || selected.get(toRaw(cid)) === 'off')
164+
let everySelected = true
165+
let noneSelected = true
166+
167+
for (const child of children.get(parent)!) {
168+
const cid = toRaw(child)
169+
170+
if (disabled.has(cid)) continue
171+
if (selected.get(cid) !== 'on') everySelected = false
172+
if (selected.get(cid) !== 'off') noneSelected = false
173+
if (!everySelected && !noneSelected) break
174+
}
163175

164176
selected.set(parent, everySelected ? 'on' : noneSelected ? 'off' : 'indeterminate')
165177

@@ -179,7 +191,7 @@ export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
179191

180192
return selected
181193
},
182-
in: (v, children, parents) => {
194+
in: (v, children, disabled, parents) => {
183195
let map = new Map()
184196

185197
for (const id of (v || [])) {
@@ -188,6 +200,7 @@ export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
188200
value: true,
189201
selected: map,
190202
children,
203+
disabled,
191204
parents,
192205
})
193206
}

0 commit comments

Comments
 (0)