diff --git a/packages/api-generator/src/types.ts b/packages/api-generator/src/types.ts index 76c410b852a..640bdf5ff52 100644 --- a/packages/api-generator/src/types.ts +++ b/packages/api-generator/src/types.ts @@ -309,6 +309,7 @@ function count (arr: string[], needle: string) { // Types that are displayed as links const allowedRefs = [ 'Anchor', + 'ActiveStrategy', 'DataIteratorItem', 'DataTableHeader', 'DataTableItem', @@ -319,9 +320,11 @@ const allowedRefs = [ 'ListItem', 'LocationStrategyFn', 'OpenSelectStrategyFn', + 'OpenStrategy', 'OpenStrategyFn', 'ScrollStrategyFn', 'SelectItemKey', + 'SelectStrategy', 'SelectStrategyFn', 'SortItem', 'SubmitEventPromise', diff --git a/packages/vuetify/src/components/VList/VList.tsx b/packages/vuetify/src/components/VList/VList.tsx index 868ee43d2cd..567728ccd1c 100644 --- a/packages/vuetify/src/components/VList/VList.tsx +++ b/packages/vuetify/src/components/VList/VList.tsx @@ -128,10 +128,10 @@ export const VList = genericComponent> itemChildren?: SelectItemKey> itemProps?: SelectItemKey> - selected?: readonly S[] - 'onUpdate:selected'?: (value: S[]) => void - opened?: readonly O[] - 'onUpdate:opened'?: (value: O[]) => void + selected?: S + 'onUpdate:selected'?: (value: S) => void + opened?: O + 'onUpdate:opened'?: (value: O) => void }, slots: VListChildrenSlots> ) => GenericProps>()({ @@ -140,9 +140,9 @@ export const VList = genericComponent true, - 'update:activated': (value: unknown[]) => true, - 'update:opened': (value: unknown[]) => true, + 'update:selected': (value: unknown) => true, + 'update:activated': (value: unknown) => true, + 'update:opened': (value: unknown) => true, 'click:open': (value: { id: unknown, value: boolean, path: unknown[] }) => true, 'click:activate': (value: { id: unknown, value: boolean, path: unknown[] }) => true, 'click:select': (value: { id: unknown, value: boolean, path: unknown[] }) => true, diff --git a/packages/vuetify/src/composables/nested/activeStrategies.ts b/packages/vuetify/src/composables/nested/activeStrategies.ts index 495cfcef2af..3487da0a779 100644 --- a/packages/vuetify/src/composables/nested/activeStrategies.ts +++ b/packages/vuetify/src/composables/nested/activeStrategies.ts @@ -1,6 +1,7 @@ /* eslint-disable sonarjs/no-identical-functions */ // Utilities import { toRaw } from 'vue' +import { wrapInArray } from '@/util' export type ActiveStrategyFn = (data: { id: unknown @@ -12,7 +13,7 @@ export type ActiveStrategyFn = (data: { }) => Set export type ActiveStrategyTransformInFn = ( - v: readonly unknown[] | undefined, + v: unknown | undefined, children: Map, parents: Map, ) => Set @@ -21,7 +22,7 @@ export type ActiveStrategyTransformOutFn = ( v: Set, children: Map, parents: Map, -) => unknown[] +) => unknown export type ActiveStrategy = { activate: ActiveStrategyFn @@ -49,14 +50,16 @@ export const independentActiveStrategy = (mandatory?: boolean): ActiveStrategy = in: (v, children, parents) => { let set = new Set() - for (const id of (v || [])) { - set = strategy.activate({ - id, - value: true, - activated: new Set(set), - children, - parents, - }) + if (v != null) { + for (const id of wrapInArray(v)) { + set = strategy.activate({ + id, + value: true, + activated: new Set(set), + children, + parents, + }) + } } return set @@ -81,8 +84,11 @@ export const independentSingleActiveStrategy = (mandatory?: boolean): ActiveStra in: (v, children, parents) => { let set = new Set() - if (v?.length) { - set = parentStrategy.in(v.slice(0, 1), children, parents) + if (v != null) { + const arr = wrapInArray(v) + if (arr.length) { + set = parentStrategy.in(arr.slice(0, 1), children, parents) + } } return set diff --git a/packages/vuetify/src/composables/nested/nested.ts b/packages/vuetify/src/composables/nested/nested.ts index aa006fa4f72..51adb784e56 100644 --- a/packages/vuetify/src/composables/nested/nested.ts +++ b/packages/vuetify/src/composables/nested/nested.ts @@ -5,7 +5,8 @@ import { useProxiedModel } from '@/composables/proxiedModel' import { computed, inject, onBeforeUnmount, provide, ref, shallowRef, toRaw, toRef } from 'vue' import { independentActiveStrategy, - independentSingleActiveStrategy, leafActiveStrategy, + independentSingleActiveStrategy, + leafActiveStrategy, leafSingleActiveStrategy, } from './activeStrategies' import { listOpenStrategy, multipleOpenStrategy, singleOpenStrategy } from './openStrategies' @@ -20,26 +21,41 @@ import { getCurrentInstance, getUid, propsFactory } from '@/util' // Types import type { InjectionKey, PropType, Ref } from 'vue' +import type { ActiveStrategy } from './activeStrategies' import type { OpenStrategy } from './openStrategies' -import type { SelectStrategyFn } from './selectStrategies' +import type { SelectStrategy } from './selectStrategies' import type { EventProp } from '@/util' -export type SelectStrategy = 'single-leaf' | 'leaf' | 'independent' | 'single-independent' | 'classic' | SelectStrategyFn +export type ActiveStrategyProp = + | 'single-leaf' + | 'leaf' + | 'independent' + | 'single-independent' + | ActiveStrategy + | ((mandatory: boolean) => ActiveStrategy) +export type SelectStrategyProp = + | 'single-leaf' + | 'leaf' + | 'independent' + | 'single-independent' + | 'classic' + | SelectStrategy + | ((mandatory: boolean) => SelectStrategy) export type OpenStrategyProp = 'single' | 'multiple' | 'list' | OpenStrategy export interface NestedProps { activatable: boolean selectable: boolean - activeStrategy: SelectStrategy | undefined - selectStrategy: SelectStrategy | undefined + activeStrategy: ActiveStrategyProp | undefined + selectStrategy: SelectStrategyProp | undefined openStrategy: OpenStrategyProp | undefined - activated: readonly unknown[] | undefined - selected: readonly unknown[] | undefined - opened: readonly unknown[] | undefined + activated: any + selected: any + opened: any mandatory: boolean - 'onUpdate:activated': EventProp<[unknown[]]> | undefined - 'onUpdate:selected': EventProp<[unknown[]]> | undefined - 'onUpdate:opened': EventProp<[unknown[]]> | undefined + 'onUpdate:activated': EventProp<[any]> | undefined + 'onUpdate:selected': EventProp<[any]> | undefined + 'onUpdate:opened': EventProp<[any]> | undefined } type NestedProvide = { @@ -88,12 +104,12 @@ export const emptyNested: NestedProvide = { export const makeNestedProps = propsFactory({ activatable: Boolean, selectable: Boolean, - activeStrategy: [String, Function] as PropType, - selectStrategy: [String, Function] as PropType, + activeStrategy: [String, Function, Object] as PropType, + selectStrategy: [String, Function, Object] as PropType, openStrategy: [String, Object] as PropType, - opened: Array as PropType, - activated: Array as PropType, - selected: Array as PropType, + opened: null, + activated: null, + selected: null, mandatory: Boolean, }, 'nested') @@ -106,6 +122,7 @@ export const useNested = (props: NestedProps) => { const activeStrategy = computed(() => { if (typeof props.activeStrategy === 'object') return props.activeStrategy + if (typeof props.activeStrategy === 'function') return props.activeStrategy(props.mandatory) switch (props.activeStrategy) { case 'leaf': return leafActiveStrategy(props.mandatory) @@ -118,6 +135,7 @@ export const useNested = (props: NestedProps) => { const selectStrategy = computed(() => { if (typeof props.selectStrategy === 'object') return props.selectStrategy + if (typeof props.selectStrategy === 'function') return props.selectStrategy(props.mandatory) switch (props.selectStrategy) { case 'single-leaf': return leafSingleSelectStrategy(props.mandatory) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeview.tsx b/packages/vuetify/src/labs/VTreeview/VTreeview.tsx index 5feb99f56d9..693d45e41aa 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeview.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeview.tsx @@ -51,9 +51,9 @@ export const VTreeview = genericComponent( props: makeVTreeviewProps(), emits: { - 'update:opened': (val: unknown[]) => true, - 'update:activated': (val: unknown[]) => true, - 'update:selected': (val: unknown[]) => true, + 'update:opened': (val: unknown) => true, + 'update:activated': (val: unknown) => true, + 'update:selected': (val: unknown) => true, 'click:open': (value: { id: unknown, value: boolean, path: unknown[] }) => true, 'click:select': (value: { id: unknown, value: boolean, path: unknown[] }) => true, },