From b49a70bcbd11a7a4347466df47ddccb1aff74992 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 10 May 2024 23:52:45 +1000 Subject: [PATCH 01/23] feat(VList): allow group activator to be activated without open children fixes #19441 --- packages/vuetify/src/components/VList/VList.tsx | 4 ++++ .../vuetify/src/components/VList/VListGroup.tsx | 2 +- .../vuetify/src/components/VList/VListItem.sass | 9 --------- .../vuetify/src/components/VList/VListItem.tsx | 14 ++++++++++---- packages/vuetify/src/composables/nested/nested.ts | 4 ++++ 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/vuetify/src/components/VList/VList.tsx b/packages/vuetify/src/components/VList/VList.tsx index 567728ccd1c..3aa5c0cf684 100644 --- a/packages/vuetify/src/components/VList/VList.tsx +++ b/packages/vuetify/src/components/VList/VList.tsx @@ -88,6 +88,10 @@ export const makeVListProps = propsFactory({ disabled: Boolean, expandIcon: String, collapseIcon: String, + groupActivatorActivatable: { + type: Boolean, + default: false, + }, lines: { type: [Boolean, String] as PropType<'one' | 'two' | 'three' | false>, default: 'one', diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index a475a86d330..02b57d3b45d 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -78,7 +78,7 @@ export const VListGroup = genericComponent()({ const toggleIcon = computed(() => isOpen.value ? props.collapseIcon : props.expandIcon) const activatorDefaults = computed(() => ({ VListItem: { - active: isOpen.value, + // active: isOpen.value, activeColor: props.activeColor, baseColor: props.baseColor, color: props.color, diff --git a/packages/vuetify/src/components/VList/VListItem.sass b/packages/vuetify/src/components/VList/VListItem.sass index cfa039d1685..ccbec17e140 100644 --- a/packages/vuetify/src/components/VList/VListItem.sass +++ b/packages/vuetify/src/components/VList/VListItem.sass @@ -318,12 +318,3 @@ .v-list-group__items .v-list-item padding-inline-start: calc(#{$base-padding} + var(--indent-padding)) !important - - .v-list-group__header.v-list-item--active - &:not(:focus-visible) - .v-list-item__overlay - opacity: 0 - - &:hover - .v-list-item__overlay - opacity: calc(#{map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier)) diff --git a/packages/vuetify/src/components/VList/VListItem.tsx b/packages/vuetify/src/components/VList/VListItem.tsx index ec51bda0e14..11127cfea91 100644 --- a/packages/vuetify/src/components/VList/VListItem.tsx +++ b/packages/vuetify/src/components/VList/VListItem.tsx @@ -28,7 +28,7 @@ import { Ripple } from '@/directives/ripple' // Utilities import { computed, watch } from 'vue' -import { deprecate, EventProp, genericComponent, propsFactory, useRender } from '@/util' +import { deprecate, EventProp, genericComponent, noop, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' @@ -173,12 +173,13 @@ export const VListItem = genericComponent()({ function onClick (e: MouseEvent) { emit('click', e) - + } + function onSelect (e: MouseEvent) { if (!isClickable.value) return link.navigate?.(e) - if (isGroupActivator) return + if (!root.groupActivatorActivatable && isGroupActivator) return if (root.activatable.value) { activate(!isActivated.value, e) @@ -241,7 +242,10 @@ export const VListItem = genericComponent()({ ]} href={ link.href.value } tabindex={ isClickable.value ? (list ? -2 : 0) : undefined } - onClick={ onClick } + onClick={[ + onSelect, + root.groupActivatorActivatable ? () => noop : onClick, + ]} onKeydown={ isClickable.value && !isLink.value && onKeyDown } v-ripple={ isClickable.value && props.ripple } > @@ -264,6 +268,7 @@ export const VListItem = genericComponent()({ key="prepend-icon" density={ props.density } icon={ props.prependIcon } + onClick={ root.groupActivatorActivatable ? onClick : () => noop } /> )} @@ -318,6 +323,7 @@ export const VListItem = genericComponent()({ key="append-icon" density={ props.density } icon={ props.appendIcon } + onClick={ root.groupActivatorActivatable ? onClick : () => noop } /> )} diff --git a/packages/vuetify/src/composables/nested/nested.ts b/packages/vuetify/src/composables/nested/nested.ts index 51adb784e56..36befce3ac2 100644 --- a/packages/vuetify/src/composables/nested/nested.ts +++ b/packages/vuetify/src/composables/nested/nested.ts @@ -45,6 +45,7 @@ export type OpenStrategyProp = 'single' | 'multiple' | 'list' | OpenStrategy export interface NestedProps { activatable: boolean + groupActivatorActivatable: boolean selectable: boolean activeStrategy: ActiveStrategyProp | undefined selectStrategy: SelectStrategyProp | undefined @@ -66,6 +67,7 @@ type NestedProvide = { parents: Ref> activatable: Ref selectable: Ref + groupActivatorActivatable: Ref opened: Ref> activated: Ref> selected: Ref> @@ -93,6 +95,7 @@ export const emptyNested: NestedProvide = { activate: () => null, select: () => null, activatable: ref(false), + groupActivatorActivatable: ref(false), selectable: ref(false), opened: ref(new Set()), activated: ref(new Set()), @@ -196,6 +199,7 @@ export const useNested = (props: NestedProps) => { root: { opened, activatable: toRef(props, 'activatable'), + groupActivatorActivatable: toRef(props, 'groupActivatorActivatable'), selectable: toRef(props, 'selectable'), activated, selected, From d13e68fc45cf7c03c3d2b4f4b4cd1415a9c60526 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sat, 11 May 2024 11:50:41 +1000 Subject: [PATCH 02/23] refactor: rollback groupActivatorActivatable & move activate group activator as treeview dedicated logic --- .../src/components/VList/VListGroup.tsx | 2 +- .../src/components/VList/VListItem.sass | 9 ++ .../src/components/VList/VListItem.tsx | 14 +-- .../vuetify/src/composables/nested/nested.ts | 5 +- .../src/labs/VTreeview/VTreeviewItem.sass | 14 +++ .../src/labs/VTreeview/VTreeviewItem.tsx | 93 ++++++++++++++++++- .../src/labs/VTreeview/_variables.scss | 1 + 7 files changed, 118 insertions(+), 20 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index 02b57d3b45d..a475a86d330 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -78,7 +78,7 @@ export const VListGroup = genericComponent()({ const toggleIcon = computed(() => isOpen.value ? props.collapseIcon : props.expandIcon) const activatorDefaults = computed(() => ({ VListItem: { - // active: isOpen.value, + active: isOpen.value, activeColor: props.activeColor, baseColor: props.baseColor, color: props.color, diff --git a/packages/vuetify/src/components/VList/VListItem.sass b/packages/vuetify/src/components/VList/VListItem.sass index ccbec17e140..c58eeab691a 100644 --- a/packages/vuetify/src/components/VList/VListItem.sass +++ b/packages/vuetify/src/components/VList/VListItem.sass @@ -318,3 +318,12 @@ .v-list-group__items .v-list-item padding-inline-start: calc(#{$base-padding} + var(--indent-padding)) !important + + // .v-list-group__header.v-list-item--active + // &:not(:focus-visible) + // .v-list-item__overlay + // opacity: 0 + + // &:hover + // .v-list-item__overlay + // opacity: calc(#{map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier)) diff --git a/packages/vuetify/src/components/VList/VListItem.tsx b/packages/vuetify/src/components/VList/VListItem.tsx index 11127cfea91..ec51bda0e14 100644 --- a/packages/vuetify/src/components/VList/VListItem.tsx +++ b/packages/vuetify/src/components/VList/VListItem.tsx @@ -28,7 +28,7 @@ import { Ripple } from '@/directives/ripple' // Utilities import { computed, watch } from 'vue' -import { deprecate, EventProp, genericComponent, noop, propsFactory, useRender } from '@/util' +import { deprecate, EventProp, genericComponent, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' @@ -173,13 +173,12 @@ export const VListItem = genericComponent()({ function onClick (e: MouseEvent) { emit('click', e) - } - function onSelect (e: MouseEvent) { + if (!isClickable.value) return link.navigate?.(e) - if (!root.groupActivatorActivatable && isGroupActivator) return + if (isGroupActivator) return if (root.activatable.value) { activate(!isActivated.value, e) @@ -242,10 +241,7 @@ export const VListItem = genericComponent()({ ]} href={ link.href.value } tabindex={ isClickable.value ? (list ? -2 : 0) : undefined } - onClick={[ - onSelect, - root.groupActivatorActivatable ? () => noop : onClick, - ]} + onClick={ onClick } onKeydown={ isClickable.value && !isLink.value && onKeyDown } v-ripple={ isClickable.value && props.ripple } > @@ -268,7 +264,6 @@ export const VListItem = genericComponent()({ key="prepend-icon" density={ props.density } icon={ props.prependIcon } - onClick={ root.groupActivatorActivatable ? onClick : () => noop } /> )} @@ -323,7 +318,6 @@ export const VListItem = genericComponent()({ key="append-icon" density={ props.density } icon={ props.appendIcon } - onClick={ root.groupActivatorActivatable ? onClick : () => noop } /> )} diff --git a/packages/vuetify/src/composables/nested/nested.ts b/packages/vuetify/src/composables/nested/nested.ts index 36befce3ac2..10f129dd55e 100644 --- a/packages/vuetify/src/composables/nested/nested.ts +++ b/packages/vuetify/src/composables/nested/nested.ts @@ -45,7 +45,6 @@ export type OpenStrategyProp = 'single' | 'multiple' | 'list' | OpenStrategy export interface NestedProps { activatable: boolean - groupActivatorActivatable: boolean selectable: boolean activeStrategy: ActiveStrategyProp | undefined selectStrategy: SelectStrategyProp | undefined @@ -67,7 +66,6 @@ type NestedProvide = { parents: Ref> activatable: Ref selectable: Ref - groupActivatorActivatable: Ref opened: Ref> activated: Ref> selected: Ref> @@ -95,7 +93,6 @@ export const emptyNested: NestedProvide = { activate: () => null, select: () => null, activatable: ref(false), - groupActivatorActivatable: ref(false), selectable: ref(false), opened: ref(new Set()), activated: ref(new Set()), @@ -199,7 +196,6 @@ export const useNested = (props: NestedProps) => { root: { opened, activatable: toRef(props, 'activatable'), - groupActivatorActivatable: toRef(props, 'groupActivatorActivatable'), selectable: toRef(props, 'selectable'), activated, selected, @@ -340,3 +336,4 @@ export const useNestedGroupActivator = () => { provide(VNestedSymbol, { ...parent, isGroupActivator: true }) } + diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass index 4cc4baaf06d..33f0515a9de 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass @@ -1,4 +1,6 @@ +@use 'sass:map' @use '../../styles/tools' +@use '../../styles/settings' @use './variables' as * @include tools.layer('components') @@ -19,3 +21,15 @@ .v-list-group__items .v-list-item--prepend // padding-inline-start: calc(#{$treeview-item-prepend-padding-inline-start} + var(--indent-padding)) !important + + .v-list-item + &--active + .v-list-item__prepend, + .v-list-item__append + > .v-badge .v-icon, + > .v-icon + opacity: #{$list-item-icon-active-opacity} + + &:not(.v-list-item--link) + .v-list-item__overlay + opacity: calc(#{map.get(settings.$states, 'activated')} * var(--v-theme-overlay-multiplier)) \ No newline at end of file diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 738bc411533..69231afd566 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -3,12 +3,14 @@ import './VTreeviewItem.sass' // Components import { VBtn } from '@/components/VBtn' -import { VListItemAction } from '@/components/VList' +import { VListItemAction, VListItemSubtitle, VListItemTitle } from '@/components/VList' import { makeVListItemProps, VListItem } from '@/components/VList/VListItem' // Composables import { IconValue } from '@/composables/icons' +import { useNestedItem } from '@/composables/nested/nested' import { useLink } from '@/composables/router' +import { genOverlays } from '@/composables/variant' // Utilities import { computed, inject, ref } from 'vue' @@ -36,6 +38,23 @@ export const VTreeviewItem = genericComponent()({ const id = computed(() => props.value === undefined ? link.href.value : props.value) const vListItemRef = ref() + const { + activate, + isActivated, + select, + isSelected, + isIndeterminate, + isGroupActivator, + root, + } = useNestedItem(id, false) + + const slotProps = computed(() => ({ + isActive: isActivated.value, + select, + isSelected: isSelected.value, + isIndeterminate: isIndeterminate.value, + } satisfies any)) + const isClickable = computed(() => !props.disabled && props.link !== false && @@ -43,8 +62,13 @@ export const VTreeviewItem = genericComponent()({ ) function onClick (e: MouseEvent | KeyboardEvent) { - if (!vListItemRef.value?.isGroupActivator || !isClickable.value) return - props.value != null && vListItemRef.value?.select(!vListItemRef.value?.isSelected, e) + if (root.activatable.value) { + activate(!isActivated.value, e) + } else if (root.selectable.value) { + select(!isSelected.value, e) + } else if (props.value != null) { + select(!isSelected.value, e) + } } function onKeyDown (e: KeyboardEvent) { @@ -57,10 +81,69 @@ export const VTreeviewItem = genericComponent()({ const visibleIds = inject(VTreeviewSymbol, { visibleIds: ref() }).visibleIds useRender(() => { + const hasTitle = (slots.title || props.title != null) + const hasSubtitle = (slots.subtitle || props.subtitle != null) const listItemProps = VListItem.filterProps(props) const hasPrepend = slots.prepend || props.toggleIcon - return ( + return isGroupActivator + ? ( +
+ <> + { genOverlays(isActivated.value, 'v-list-item') } + { props.toggleIcon && ( + + + {{ + loader () { + return ( + + ) + }, + }} + + + )} + + + +
+ { hasTitle && ( + + { slots.title?.({ title: props.title }) ?? props.title } + + )} + + { hasSubtitle && ( + + { slots.subtitle?.({ subtitle: props.subtitle }) ?? props.subtitle } + + )} + + { slots.default?.(slotProps.value) } +
+
+ ) : ( ()({ } : undefined, }} - ) + ) }) return {} diff --git a/packages/vuetify/src/labs/VTreeview/_variables.scss b/packages/vuetify/src/labs/VTreeview/_variables.scss index ce050d3b605..2d9db3bb2da 100644 --- a/packages/vuetify/src/labs/VTreeview/_variables.scss +++ b/packages/vuetify/src/labs/VTreeview/_variables.scss @@ -1,4 +1,5 @@ @use 'sass:map'; +@forward '../../components/VList/variables'; $treeview-group-list-indent-size: 16px !default; $treeview-group-list-prepend-width: 16px !default; From cae4b0bff018eab4915c3fa44f4dff74cb9a417a Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sat, 11 May 2024 11:51:59 +1000 Subject: [PATCH 03/23] refactor: remove groupActivatorActivatable from VList --- packages/vuetify/src/components/VList/VList.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/vuetify/src/components/VList/VList.tsx b/packages/vuetify/src/components/VList/VList.tsx index 3aa5c0cf684..567728ccd1c 100644 --- a/packages/vuetify/src/components/VList/VList.tsx +++ b/packages/vuetify/src/components/VList/VList.tsx @@ -88,10 +88,6 @@ export const makeVListProps = propsFactory({ disabled: Boolean, expandIcon: String, collapseIcon: String, - groupActivatorActivatable: { - type: Boolean, - default: false, - }, lines: { type: [Boolean, String] as PropType<'one' | 'two' | 'three' | false>, default: 'one', From a911942f48156a63ee05708a76fe6c32dc3770bc Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sat, 11 May 2024 21:55:59 +1000 Subject: [PATCH 04/23] chore: introduce open-on-click prop & improve logic --- .../vuetify/src/components/VList/VListItem.sass | 16 ++++++++-------- .../src/labs/VTreeview/VTreeviewChildren.tsx | 2 ++ .../src/labs/VTreeview/VTreeviewItem.sass | 12 ------------ .../vuetify/src/labs/VTreeview/VTreeviewItem.tsx | 12 +++++++++--- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListItem.sass b/packages/vuetify/src/components/VList/VListItem.sass index c58eeab691a..a57db323ea0 100644 --- a/packages/vuetify/src/components/VList/VListItem.sass +++ b/packages/vuetify/src/components/VList/VListItem.sass @@ -319,11 +319,11 @@ .v-list-group__items .v-list-item padding-inline-start: calc(#{$base-padding} + var(--indent-padding)) !important - // .v-list-group__header.v-list-item--active - // &:not(:focus-visible) - // .v-list-item__overlay - // opacity: 0 - - // &:hover - // .v-list-item__overlay - // opacity: calc(#{map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier)) + .v-list-group__header:not(.v-treeview-item).v-list-item--active + &:not(:focus-visible) + .v-list-item__overlay + opacity: 0 + + &:hover + .v-list-item__overlay + opacity: calc(#{map.get(settings.$states, 'hover')} * var(--v-theme-overlay-multiplier)) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index a9bfd2c6ca6..41f2aa05abc 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -21,6 +21,7 @@ export type VTreeviewChildrenSlots = { } export const makeVTreeviewChildrenProps = propsFactory({ + openOnClick: Boolean, loadChildren: Function as PropType<(item: unknown) => Promise>, loadingIcon: { type: String, @@ -97,6 +98,7 @@ export const VTreeviewChildren = genericComponent onClick(e, item) } /> diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass index 33f0515a9de..769bf783f20 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass @@ -21,15 +21,3 @@ .v-list-group__items .v-list-item--prepend // padding-inline-start: calc(#{$treeview-item-prepend-padding-inline-start} + var(--indent-padding)) !important - - .v-list-item - &--active - .v-list-item__prepend, - .v-list-item__append - > .v-badge .v-icon, - > .v-icon - opacity: #{$list-item-icon-active-opacity} - - &:not(.v-list-item--link) - .v-list-item__overlay - opacity: calc(#{map.get(settings.$states, 'activated')} * var(--v-theme-overlay-multiplier)) \ No newline at end of file diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 69231afd566..0ba53361b1e 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -14,7 +14,7 @@ import { genOverlays } from '@/composables/variant' // Utilities import { computed, inject, ref } from 'vue' -import { genericComponent, propsFactory, useRender } from '@/util' +import { genericComponent, noop, propsFactory, useRender } from '@/util' // Types import { VTreeviewSymbol } from './shared' @@ -24,6 +24,7 @@ import type { VListItemSlots } from '@/components/VList/VListItem' export const makeVTreeviewItemProps = propsFactory({ loading: Boolean, toggleIcon: IconValue, + openOnClick: Boolean, ...makeVListItemProps({ slim: true }), }, 'VTreeviewItem') @@ -48,6 +49,8 @@ export const VTreeviewItem = genericComponent()({ root, } = useNestedItem(id, false) + const isActivetableGroupActivator = computed(() => isGroupActivator && !props.openOnClick) + const slotProps = computed(() => ({ isActive: isActivated.value, select, @@ -62,6 +65,8 @@ export const VTreeviewItem = genericComponent()({ ) function onClick (e: MouseEvent | KeyboardEvent) { + if (!isActivetableGroupActivator.value) return + if (root.activatable.value) { activate(!isActivated.value, e) } else if (root.selectable.value) { @@ -86,7 +91,7 @@ export const VTreeviewItem = genericComponent()({ const listItemProps = VListItem.filterProps(props) const hasPrepend = slots.prepend || props.toggleIcon - return isGroupActivator + return isActivetableGroupActivator.value ? (
()({ props.class, ]} onClick={ onClick } + v-ripple={ isClickable.value && props.ripple } > <> { genOverlays(isActivated.value, 'v-list-item') } @@ -154,7 +160,7 @@ export const VTreeviewItem = genericComponent()({ }, props.class, ]} - onClick={ onClick } + onClick={ () => noop } onKeydown={ isClickable.value && onKeyDown } > {{ From bf1dd2b3067e6e3fdce9acb65f76f7274ae58676 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sun, 12 May 2024 11:00:04 +1000 Subject: [PATCH 05/23] chore: achieve selectable same as activable --- .../src/components/VList/VListItem.sass | 2 +- .../src/components/VList/VListItem.tsx | 2 ++ .../src/labs/VTreeview/VTreeviewItem.tsx | 33 +++++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListItem.sass b/packages/vuetify/src/components/VList/VListItem.sass index a57db323ea0..d1146c26a47 100644 --- a/packages/vuetify/src/components/VList/VListItem.sass +++ b/packages/vuetify/src/components/VList/VListItem.sass @@ -319,7 +319,7 @@ .v-list-group__items .v-list-item padding-inline-start: calc(#{$base-padding} + var(--indent-padding)) !important - .v-list-group__header:not(.v-treeview-item).v-list-item--active + .v-list-group__header:not(.v-treeview-item--activetable-group-activator).v-list-item--active &:not(:focus-visible) .v-list-item__overlay opacity: 0 diff --git a/packages/vuetify/src/components/VList/VListItem.tsx b/packages/vuetify/src/components/VList/VListItem.tsx index ec51bda0e14..6bc8fc8bc64 100644 --- a/packages/vuetify/src/components/VList/VListItem.tsx +++ b/packages/vuetify/src/components/VList/VListItem.tsx @@ -359,6 +359,8 @@ export const VListItem = genericComponent()({ }) return { + activate, + isActivated, isGroupActivator, isSelected, list, diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 0ba53361b1e..5db80818a4e 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -7,6 +7,7 @@ import { VListItemAction, VListItemSubtitle, VListItemTitle } from '@/components import { makeVListItemProps, VListItem } from '@/components/VList/VListItem' // Composables +import { useDensity } from '@/composables/density' import { IconValue } from '@/composables/icons' import { useNestedItem } from '@/composables/nested/nested' import { useLink } from '@/composables/router' @@ -14,7 +15,7 @@ import { genOverlays } from '@/composables/variant' // Utilities import { computed, inject, ref } from 'vue' -import { genericComponent, noop, propsFactory, useRender } from '@/util' +import { genericComponent, propsFactory, useRender } from '@/util' // Types import { VTreeviewSymbol } from './shared' @@ -49,7 +50,9 @@ export const VTreeviewItem = genericComponent()({ root, } = useNestedItem(id, false) - const isActivetableGroupActivator = computed(() => isGroupActivator && !props.openOnClick) + const isActivetableGroupActivator = computed(() => (root.activatable || root.selectable) && isGroupActivator && !props.openOnClick) + + const { densityClasses } = useDensity(props, 'v-list-item') const slotProps = computed(() => ({ isActive: isActivated.value, @@ -65,14 +68,20 @@ export const VTreeviewItem = genericComponent()({ ) function onClick (e: MouseEvent | KeyboardEvent) { - if (!isActivetableGroupActivator.value) return + if (!isActivetableGroupActivator.value && isGroupActivator) return if (root.activatable.value) { - activate(!isActivated.value, e) + if (isActivetableGroupActivator.value) { + activate(!isActivated.value, e) + } else { + vListItemRef.value?.activate(!vListItemRef.value?.isActivated, e) + } } else if (root.selectable.value) { - select(!isSelected.value, e) - } else if (props.value != null) { - select(!isSelected.value, e) + if (isActivetableGroupActivator.value) { + select(!isSelected.value, e) + } else { + vListItemRef.value?.select(!vListItemRef.value?.isSelected, e) + } } } @@ -96,17 +105,21 @@ export const VTreeviewItem = genericComponent()({
<> - { genOverlays(isActivated.value, 'v-list-item') } + { genOverlays(isActivated.value || isSelected.value, 'v-list-item') } { props.toggleIcon && ( ()({ }, props.class, ]} - onClick={ () => noop } + onClick={ onClick } onKeydown={ isClickable.value && onKeyDown } > {{ From 7f3811a072c739f0ad8484cb20bcd909d0159594 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sun, 12 May 2024 11:17:38 +1000 Subject: [PATCH 06/23] chore: fix lint error & rollback redundant changes --- packages/vuetify/src/composables/nested/nested.ts | 1 - packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass | 2 -- packages/vuetify/src/labs/VTreeview/_variables.scss | 1 - 3 files changed, 4 deletions(-) diff --git a/packages/vuetify/src/composables/nested/nested.ts b/packages/vuetify/src/composables/nested/nested.ts index 10f129dd55e..51adb784e56 100644 --- a/packages/vuetify/src/composables/nested/nested.ts +++ b/packages/vuetify/src/composables/nested/nested.ts @@ -336,4 +336,3 @@ export const useNestedGroupActivator = () => { provide(VNestedSymbol, { ...parent, isGroupActivator: true }) } - diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass index 769bf783f20..4cc4baaf06d 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.sass @@ -1,6 +1,4 @@ -@use 'sass:map' @use '../../styles/tools' -@use '../../styles/settings' @use './variables' as * @include tools.layer('components') diff --git a/packages/vuetify/src/labs/VTreeview/_variables.scss b/packages/vuetify/src/labs/VTreeview/_variables.scss index 2d9db3bb2da..ce050d3b605 100644 --- a/packages/vuetify/src/labs/VTreeview/_variables.scss +++ b/packages/vuetify/src/labs/VTreeview/_variables.scss @@ -1,5 +1,4 @@ @use 'sass:map'; -@forward '../../components/VList/variables'; $treeview-group-list-indent-size: 16px !default; $treeview-group-list-prepend-width: 16px !default; From 52385b1539a5ed71bfdd142717f75b0e36431260 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sun, 12 May 2024 11:51:10 +1000 Subject: [PATCH 07/23] chore: fix known issue & improve --- .../src/labs/VTreeview/VTreeviewItem.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 5db80818a4e..a4b566af5f9 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -50,7 +50,11 @@ export const VTreeviewItem = genericComponent()({ root, } = useNestedItem(id, false) - const isActivetableGroupActivator = computed(() => (root.activatable || root.selectable) && isGroupActivator && !props.openOnClick) + const isActivetableGroupActivator = computed(() => + (root.activatable.value || root.selectable.value) && + isGroupActivator && + !props.openOnClick + ) const { densityClasses } = useDensity(props, 'v-list-item') @@ -67,8 +71,11 @@ export const VTreeviewItem = genericComponent()({ (props.link || link.isClickable.value || (props.value != null && !!vListItemRef.value?.list)) ) - function onClick (e: MouseEvent | KeyboardEvent) { - if (!isActivetableGroupActivator.value && isGroupActivator) return + function selectItem (e: MouseEvent | KeyboardEvent) { + if ( + !isClickable.value || + (!isActivetableGroupActivator.value && isGroupActivator) + ) return if (root.activatable.value) { if (isActivetableGroupActivator.value) { @@ -88,7 +95,7 @@ export const VTreeviewItem = genericComponent()({ function onKeyDown (e: KeyboardEvent) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() - onClick(e as any as MouseEvent) + selectItem(e) } } @@ -115,7 +122,7 @@ export const VTreeviewItem = genericComponent()({ densityClasses.value, props.class, ]} - onClick={ onClick } + onClick={ selectItem } v-ripple={ isClickable.value && props.ripple } > <> @@ -173,7 +180,7 @@ export const VTreeviewItem = genericComponent()({ }, props.class, ]} - onClick={ onClick } + onClick={ selectItem } onKeydown={ isClickable.value && onKeyDown } > {{ From b09290cb8318ce32d554f35258addc9f01626007 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sun, 12 May 2024 22:20:51 +1000 Subject: [PATCH 08/23] chore: remove selectable from the concern of this PR --- .../vuetify/src/components/VList/VListItem.tsx | 2 +- .../vuetify/src/labs/VTreeview/VTreeviewItem.tsx | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListItem.tsx b/packages/vuetify/src/components/VList/VListItem.tsx index 6bc8fc8bc64..5033f7954e1 100644 --- a/packages/vuetify/src/components/VList/VListItem.tsx +++ b/packages/vuetify/src/components/VList/VListItem.tsx @@ -34,7 +34,7 @@ import { deprecate, EventProp, genericComponent, propsFactory, useRender } from import type { PropType } from 'vue' import type { RippleDirectiveBinding } from '@/directives/ripple' -type ListItemSlot = { +export type ListItemSlot = { isActive: boolean isSelected: boolean isIndeterminate: boolean diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index a4b566af5f9..1cd911955df 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -20,7 +20,7 @@ import { genericComponent, propsFactory, useRender } from '@/util' // Types import { VTreeviewSymbol } from './shared' import { VProgressCircular } from '../allComponents' -import type { VListItemSlots } from '@/components/VList/VListItem' +import type { ListItemSlot, VListItemSlots } from '@/components/VList/VListItem' export const makeVTreeviewItemProps = propsFactory({ loading: Boolean, @@ -51,7 +51,7 @@ export const VTreeviewItem = genericComponent()({ } = useNestedItem(id, false) const isActivetableGroupActivator = computed(() => - (root.activatable.value || root.selectable.value) && + (root.activatable.value) && isGroupActivator && !props.openOnClick ) @@ -63,7 +63,7 @@ export const VTreeviewItem = genericComponent()({ select, isSelected: isSelected.value, isIndeterminate: isIndeterminate.value, - } satisfies any)) + } satisfies ListItemSlot)) const isClickable = computed(() => !props.disabled && @@ -83,12 +83,8 @@ export const VTreeviewItem = genericComponent()({ } else { vListItemRef.value?.activate(!vListItemRef.value?.isActivated, e) } - } else if (root.selectable.value) { - if (isActivetableGroupActivator.value) { - select(!isSelected.value, e) - } else { - vListItemRef.value?.select(!vListItemRef.value?.isSelected, e) - } + } else if (props.value != null) { + vListItemRef.value?.select(!vListItemRef.value?.isSelected, e) } } From c7d296dad1ba715504902635d0071c4ecb7a02ca Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 11:03:02 +1000 Subject: [PATCH 09/23] fix: selectable is initially working --- .../src/components/VList/VListGroup.tsx | 4 +-- .../src/labs/VTreeview/VTreeviewChildren.tsx | 30 +++++++++++-------- .../src/labs/VTreeview/VTreeviewItem.tsx | 14 +++++---- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index a475a86d330..59bb880c2a9 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -17,7 +17,7 @@ import { defineComponent, genericComponent, propsFactory, useRender } from '@/ut export type VListGroupSlots = { default: never - activator: { isOpen: boolean, props: Record } + activator: { id: any, isOpen: boolean, props: Record } } const VListGroupActivator = defineComponent({ @@ -106,7 +106,7 @@ export const VListGroup = genericComponent()({ { slots.activator && ( - { slots.activator({ props: activatorProps.value, isOpen: isOpen.value }) } + { slots.activator({ id: _id, props: activatorProps.value, isOpen: isOpen.value }) } )} diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index 41f2aa05abc..bbf0361ec26 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -58,9 +58,13 @@ export const VTreeviewChildren = genericComponent void, isSelected: boolean) { e.stopPropagation() + if (props.selectable) { + select(!isSelected) + } + checkChildren(item) } @@ -69,15 +73,17 @@ export const VTreeviewChildren = genericComponent slots.prepend?.({ ...slotProps, item }) : props.selectable - ? ({ isSelected, isIndeterminate }) => ( - onClick(e, item) } - /> + ? ({ isSelected, isIndeterminate, select }) => ( +
+ selectItem(e, item, select, isSelected) } + /> +
) : undefined, append: slots.append ? slotProps => slots.append?.({ ...slotProps, item }) : undefined, @@ -93,14 +99,14 @@ export const VTreeviewChildren = genericComponent {{ - activator: ({ props: activatorProps }) => ( + activator: ({ props: activatorProps, id }) => ( onClick(e, item) } + value={ id } /> ), default: () => ( diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 1cd911955df..75ec960d5ab 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -37,7 +37,7 @@ export const VTreeviewItem = genericComponent()({ setup (props, { attrs, slots, emit }) { const link = useLink(props, attrs) - const id = computed(() => props.value === undefined ? link.href.value : props.value) + const rawId = computed(() => props.value === undefined ? link.href.value : props.value) const vListItemRef = ref() const { @@ -48,7 +48,8 @@ export const VTreeviewItem = genericComponent()({ isIndeterminate, isGroupActivator, root, - } = useNestedItem(id, false) + id, + } = useNestedItem(rawId, false) const isActivetableGroupActivator = computed(() => (root.activatable.value) && @@ -71,7 +72,7 @@ export const VTreeviewItem = genericComponent()({ (props.link || link.isClickable.value || (props.value != null && !!vListItemRef.value?.list)) ) - function selectItem (e: MouseEvent | KeyboardEvent) { + function activateItem (e: MouseEvent | KeyboardEvent) { if ( !isClickable.value || (!isActivetableGroupActivator.value && isGroupActivator) @@ -91,7 +92,7 @@ export const VTreeviewItem = genericComponent()({ function onKeyDown (e: KeyboardEvent) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() - selectItem(e) + activateItem(e) } } @@ -118,7 +119,7 @@ export const VTreeviewItem = genericComponent()({ densityClasses.value, props.class, ]} - onClick={ selectItem } + onClick={ activateItem } v-ripple={ isClickable.value && props.ripple } > <> @@ -176,7 +177,8 @@ export const VTreeviewItem = genericComponent()({ }, props.class, ]} - onClick={ selectItem } + value={ id.value.value } + onClick={ activateItem } onKeydown={ isClickable.value && onKeyDown } > {{ From 91a763f891c4de9101a708f9bd79b4f167d5ee2d Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 11:16:44 +1000 Subject: [PATCH 10/23] fix: classic select strategy works --- packages/vuetify/src/components/VList/VListGroup.tsx | 2 +- packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index 59bb880c2a9..b4b76a7d183 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -106,7 +106,7 @@ export const VListGroup = genericComponent()({ { slots.activator && ( - { slots.activator({ id: _id, props: activatorProps.value, isOpen: isOpen.value }) } + { slots.activator({ id: _id.value, props: activatorProps.value, isOpen: isOpen.value }) } )} diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 75ec960d5ab..601b58e8918 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -177,7 +177,7 @@ export const VTreeviewItem = genericComponent()({ }, props.class, ]} - value={ id.value.value } + value={ id.value } onClick={ activateItem } onKeydown={ isClickable.value && onKeyDown } > From 673f6cb6a52bc6d27baa105ab0edb3dbea6fb3e2 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 11:31:56 +1000 Subject: [PATCH 11/23] fix: selectable is fully working --- packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index bbf0361ec26..c1daeb9c5c5 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -11,6 +11,7 @@ import { genericComponent, propsFactory } from '@/util' import type { PropType } from 'vue' import type { InternalListItem } from '@/components/VList/VList' import type { VListItemSlots } from '@/components/VList/VListItem' +import type { SelectStrategyProp } from '@/composables/nested/nested' import type { GenericProps } from '@/util' export type VTreeviewChildrenSlots = { @@ -29,6 +30,7 @@ export const makeVTreeviewChildrenProps = propsFactory({ }, items: Array as PropType, selectable: Boolean, + selectStrategy: [String, Function, Object] as PropType, }, 'VTreeviewChildren') export const VTreeviewChildren = genericComponent( @@ -72,7 +74,7 @@ export const VTreeviewChildren = genericComponent slots.prepend?.({ ...slotProps, item }) - : props.selectable + : props.selectable && (!children || (children && props.selectStrategy !== 'single-leaf' && props.selectStrategy !== 'leaf')) ? ({ isSelected, isIndeterminate, select }) => (
Date: Fri, 17 May 2024 11:43:20 +1000 Subject: [PATCH 12/23] chore: remove open-on-click logic --- packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 2 -- packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index c1daeb9c5c5..5310988f167 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -22,7 +22,6 @@ export type VTreeviewChildrenSlots = { } export const makeVTreeviewChildrenProps = propsFactory({ - openOnClick: Boolean, loadChildren: Function as PropType<(item: unknown) => Promise>, loadingIcon: { type: String, @@ -106,7 +105,6 @@ export const VTreeviewChildren = genericComponent diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 601b58e8918..378814d4d42 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -25,7 +25,6 @@ import type { ListItemSlot, VListItemSlots } from '@/components/VList/VListItem' export const makeVTreeviewItemProps = propsFactory({ loading: Boolean, toggleIcon: IconValue, - openOnClick: Boolean, ...makeVListItemProps({ slim: true }), }, 'VTreeviewItem') @@ -53,8 +52,7 @@ export const VTreeviewItem = genericComponent()({ const isActivetableGroupActivator = computed(() => (root.activatable.value) && - isGroupActivator && - !props.openOnClick + isGroupActivator ) const { densityClasses } = useDensity(props, 'v-list-item') @@ -84,8 +82,6 @@ export const VTreeviewItem = genericComponent()({ } else { vListItemRef.value?.activate(!vListItemRef.value?.isActivated, e) } - } else if (props.value != null) { - vListItemRef.value?.select(!vListItemRef.value?.isSelected, e) } } From 893db033d1afd72a093ee404ebb94be0f1276ef6 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 12:43:47 +1000 Subject: [PATCH 13/23] chore: load children when click group activator --- packages/vuetify/src/labs/VTreeview/VTreeview.tsx | 2 +- .../vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeview.tsx b/packages/vuetify/src/labs/VTreeview/VTreeview.tsx index 693d45e41aa..daaa14820f2 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeview.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeview.tsx @@ -34,7 +34,7 @@ export const makeVTreeviewProps = propsFactory({ ...omit(makeVListProps({ collapseIcon: '$treeviewCollapse', expandIcon: '$treeviewExpand', - selectStrategy: 'independent' as const, + selectStrategy: 'classic' as const, openStrategy: 'multiple' as const, slim: true, }), ['nav']), diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index 415cd582faa..d8c970362d1 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -4,7 +4,7 @@ import { VTreeviewItem } from './VTreeviewItem' import { VCheckboxBtn } from '@/components/VCheckbox' // Utilities -import { shallowRef } from 'vue' +import { shallowRef, withModifiers } from 'vue' import { genericComponent, propsFactory } from '@/util' // Types @@ -62,14 +62,10 @@ export const VTreeviewChildren = genericComponent void, isSelected: boolean) { - e.stopPropagation() - + function selectItem (select: (value: boolean) => void, isSelected: boolean) { if (props.selectable) { select(!isSelected) } - - checkChildren(item) } return () => slots.default?.() ?? props.items?.map(({ children, props: itemProps, raw: item }) => { @@ -86,7 +82,7 @@ export const VTreeviewChildren = genericComponent selectItem(e, item, select, isSelected) } + onClick={ withModifiers(() => selectItem(select, isSelected), ['stop']) } />
) @@ -111,6 +107,7 @@ export const VTreeviewChildren = genericComponent () => checkChildren(item), ['stop']) } /> ), default: () => ( From 83b164380aa7d63321f7a1faf8b697db0b565f96 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 13:04:38 +1000 Subject: [PATCH 14/23] chore(VTreeviewChildren): allow prepend slot to display infront of select checkbox --- .../src/labs/VTreeview/VTreeviewChildren.tsx | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index d8c970362d1..7bfdfad301b 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -71,22 +71,27 @@ export const VTreeviewChildren = genericComponent slots.default?.() ?? props.items?.map(({ children, props: itemProps, raw: item }) => { const loading = isLoading.value === item.value const slotsWithItem = { - prepend: slots.prepend - ? slotProps => slots.prepend?.({ ...slotProps, item }) - : props.selectable && (!children || (children && props.selectStrategy !== 'single-leaf' && props.selectStrategy !== 'leaf')) - ? ({ isSelected, isIndeterminate, select }) => ( -
- selectItem(select, isSelected), ['stop']) } - /> -
- ) - : undefined, + prepend: slotProps => ( + <> + { slots.prepend?.({ ...slotProps, item }) } + { + props.selectable && (!children || (children && props.selectStrategy !== 'single-leaf' && props.selectStrategy !== 'leaf')) + ? ( +
+ selectItem(slotProps.select, slotProps.isSelected), ['stop']) } + /> +
+ ) + : undefined + } + + ), append: slots.append ? slotProps => slots.append?.({ ...slotProps, item }) : undefined, title: slots.title ? slotProps => slots.title?.({ ...slotProps, item }) : undefined, } satisfies VTreeviewItem['$props']['$children'] @@ -107,7 +112,7 @@ export const VTreeviewChildren = genericComponent () => checkChildren(item), ['stop']) } + onClick={ () => checkChildren(item) } /> ), default: () => ( From 9316bcd9b35e51fc9642048fb63dbc6babcb45ae Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 13:34:36 +1000 Subject: [PATCH 15/23] chore: improve naming & typing --- packages/vuetify/src/components/VList/VListGroup.tsx | 8 ++++---- packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index b4b76a7d183..b7a8b5a7526 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -17,7 +17,7 @@ import { defineComponent, genericComponent, propsFactory, useRender } from '@/ut export type VListGroupSlots = { default: never - activator: { id: any, isOpen: boolean, props: Record } + activator: { nestedId: unknown, isOpen: boolean, props: Record } } const VListGroupActivator = defineComponent({ @@ -60,8 +60,8 @@ export const VListGroup = genericComponent()({ props: makeVListGroupProps(), setup (props, { slots }) { - const { isOpen, open, id: _id } = useNestedItem(toRef(props, 'value'), true) - const id = computed(() => `v-list-group--id-${String(_id.value)}`) + const { isOpen, open, id: nestedId } = useNestedItem(toRef(props, 'value'), true) + const id = computed(() => `v-list-group--id-${String(nestedId.value)}`) const list = useList() const { isBooted } = useSsrBoot() @@ -106,7 +106,7 @@ export const VListGroup = genericComponent()({ { slots.activator && ( - { slots.activator({ id: _id.value, props: activatorProps.value, isOpen: isOpen.value }) } + { slots.activator({ nestedId: nestedId.value, props: activatorProps.value, isOpen: isOpen.value }) } )} diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index 7bfdfad301b..627f4250a2f 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -105,13 +105,13 @@ export const VTreeviewChildren = genericComponent {{ - activator: ({ props: activatorProps, id }) => ( + activator: ({ props: activatorProps, nestedId }) => ( checkChildren(item) } /> ), From a8914f9a9ceb0c23024931b9238714dae75b37cb Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 16:03:54 +1000 Subject: [PATCH 16/23] fix: move prepend slot after tree view item checkbox --- packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index 627f4250a2f..0f657a8f985 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -73,7 +73,6 @@ export const VTreeviewChildren = genericComponent ( <> - { slots.prepend?.({ ...slotProps, item }) } { props.selectable && (!children || (children && props.selectStrategy !== 'single-leaf' && props.selectStrategy !== 'leaf')) ? ( @@ -90,6 +89,7 @@ export const VTreeviewChildren = genericComponent ), append: slots.append ? slotProps => slots.append?.({ ...slotProps, item }) : undefined, From 0592d49cea56ab9ddb5f6e7dcf36e32d21f138ba Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Fri, 17 May 2024 16:46:31 +1000 Subject: [PATCH 17/23] fix: fix failed cy test --- .../src/components/VList/__tests__/VListGroup.spec.cy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx b/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx index 89535931f7a..32247b50c2b 100644 --- a/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx +++ b/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx @@ -17,7 +17,7 @@ describe('VListGroup', () => { - {{ activator: props => }} + {{ activator: props => }} From d4c9845467396f2e1f9694ab26a81339e9d6efa9 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Tue, 21 May 2024 21:30:41 +1000 Subject: [PATCH 18/23] chore: remove nestedId --- .../src/components/VList/VListGroup.tsx | 8 +++--- .../src/labs/VTreeview/VTreeviewChildren.tsx | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index b7a8b5a7526..a475a86d330 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -17,7 +17,7 @@ import { defineComponent, genericComponent, propsFactory, useRender } from '@/ut export type VListGroupSlots = { default: never - activator: { nestedId: unknown, isOpen: boolean, props: Record } + activator: { isOpen: boolean, props: Record } } const VListGroupActivator = defineComponent({ @@ -60,8 +60,8 @@ export const VListGroup = genericComponent()({ props: makeVListGroupProps(), setup (props, { slots }) { - const { isOpen, open, id: nestedId } = useNestedItem(toRef(props, 'value'), true) - const id = computed(() => `v-list-group--id-${String(nestedId.value)}`) + const { isOpen, open, id: _id } = useNestedItem(toRef(props, 'value'), true) + const id = computed(() => `v-list-group--id-${String(_id.value)}`) const list = useList() const { isBooted } = useSsrBoot() @@ -106,7 +106,7 @@ export const VListGroup = genericComponent()({ { slots.activator && ( - { slots.activator({ nestedId: nestedId.value, props: activatorProps.value, isOpen: isOpen.value }) } + { slots.activator({ props: activatorProps.value, isOpen: isOpen.value }) } )} diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index 0f657a8f985..b95658b1a77 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -105,16 +105,22 @@ export const VTreeviewChildren = genericComponent {{ - activator: ({ props: activatorProps, nestedId }) => ( - checkChildren(item) } - /> - ), + activator: ({ props: activatorProps }) => { + const listItemProps = { + ...itemProps, + ...activatorProps, + value: itemProps?.value, + } + + return ( + checkChildren(item) } + /> + ) + }, default: () => ( Date: Tue, 21 May 2024 09:32:48 -0500 Subject: [PATCH 19/23] chore(VTreeviewChildren): code refactor --- .../src/labs/VTreeview/VTreeviewChildren.tsx | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index b95658b1a77..ef46c1652d3 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -73,22 +73,19 @@ export const VTreeviewChildren = genericComponent ( <> - { - props.selectable && (!children || (children && props.selectStrategy !== 'single-leaf' && props.selectStrategy !== 'leaf')) - ? ( -
- selectItem(slotProps.select, slotProps.isSelected), ['stop']) } - /> -
- ) - : undefined - } + { props.selectable && (!children || (children && !['leaf', 'single-leaf'].includes(props.selectStrategy as string))) && ( +
+ selectItem(slotProps.select, slotProps.isSelected), ['stop']) } + /> +
+ )} + { slots.prepend?.({ ...slotProps, item }) } ), From 13a914173767de66e30d82e9a11b040e0c475b01 Mon Sep 17 00:00:00 2001 From: John Leider Date: Tue, 21 May 2024 09:44:11 -0500 Subject: [PATCH 20/23] chore(VTreeviewItem): change variable name --- packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx index 378814d4d42..d4ecc2128a3 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewItem.tsx @@ -50,7 +50,7 @@ export const VTreeviewItem = genericComponent()({ id, } = useNestedItem(rawId, false) - const isActivetableGroupActivator = computed(() => + const isActivatableGroupActivator = computed(() => (root.activatable.value) && isGroupActivator ) @@ -73,11 +73,11 @@ export const VTreeviewItem = genericComponent()({ function activateItem (e: MouseEvent | KeyboardEvent) { if ( !isClickable.value || - (!isActivetableGroupActivator.value && isGroupActivator) + (!isActivatableGroupActivator.value && isGroupActivator) ) return if (root.activatable.value) { - if (isActivetableGroupActivator.value) { + if (isActivatableGroupActivator.value) { activate(!isActivated.value, e) } else { vListItemRef.value?.activate(!vListItemRef.value?.isActivated, e) @@ -100,7 +100,7 @@ export const VTreeviewItem = genericComponent()({ const listItemProps = VListItem.filterProps(props) const hasPrepend = slots.prepend || props.toggleIcon - return isActivetableGroupActivator.value + return isActivatableGroupActivator.value ? (
Date: Wed, 22 May 2024 21:22:57 +1000 Subject: [PATCH 21/23] fix: support onClick:open & onClick:select --- packages/vuetify/src/components/VList/VList.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/vuetify/src/components/VList/VList.tsx b/packages/vuetify/src/components/VList/VList.tsx index 567728ccd1c..00e020b2a94 100644 --- a/packages/vuetify/src/components/VList/VList.tsx +++ b/packages/vuetify/src/components/VList/VList.tsx @@ -22,7 +22,7 @@ import { makeVariantProps } from '@/composables/variant' // Utilities import { computed, ref, shallowRef, toRef } from 'vue' -import { focusChild, genericComponent, getPropertyFromItem, omit, propsFactory, useRender } from '@/util' +import { EventProp, focusChild, genericComponent, getPropertyFromItem, omit, propsFactory, useRender } from '@/util' // Types import type { PropType } from 'vue' @@ -95,6 +95,8 @@ export const makeVListProps = propsFactory({ slim: Boolean, nav: Boolean, + 'onClick:open': EventProp<[{ id: unknown, value: boolean, path: unknown[] }]>(), + 'onClick:select': EventProp<[{ id: unknown, value: boolean, path: unknown[] }]>(), ...makeNestedProps({ selectStrategy: 'single-leaf' as const, openStrategy: 'list' as const, @@ -130,6 +132,8 @@ export const VList = genericComponent> selected?: S 'onUpdate:selected'?: (value: S) => void + 'onClick:open'?: (value: { id: unknown, value: boolean, path: unknown[] }) => void + 'onClick:select'?: (value: { id: unknown, value: boolean, path: unknown[] }) => void opened?: O 'onUpdate:opened'?: (value: O) => void }, From ff1e855e906baf8b68b41559af5137ad56a85470 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Wed, 22 May 2024 21:56:50 +1000 Subject: [PATCH 22/23] fix: keyboard navigation when using selectable group activator --- .../src/components/VList/__tests__/VListGroup.spec.cy.tsx | 2 +- packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx b/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx index 32247b50c2b..89535931f7a 100644 --- a/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx +++ b/packages/vuetify/src/components/VList/__tests__/VListGroup.spec.cy.tsx @@ -17,7 +17,7 @@ describe('VListGroup', () => { - {{ activator: props => }} + {{ activator: props => }} diff --git a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx index ef46c1652d3..17d40421270 100644 --- a/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx +++ b/packages/vuetify/src/labs/VTreeview/VTreeviewChildren.tsx @@ -77,11 +77,15 @@ export const VTreeviewChildren = genericComponent selectItem(slotProps.select, slotProps.isSelected), ['stop']) } + onKeydown={ (e: KeyboardEvent) => { + if (!['Enter', 'Space'].includes(e.key)) return + e.stopPropagation() + selectItem(slotProps.select, slotProps.isSelected) + }} />
)} From ab96106d280e81639fbc8b175b164812cf67f1fe Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Mon, 27 May 2024 21:32:15 +1000 Subject: [PATCH 23/23] fix: prevent clicking open button activate the item --- packages/vuetify/src/components/VList/VListGroup.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vuetify/src/components/VList/VListGroup.tsx b/packages/vuetify/src/components/VList/VListGroup.tsx index a475a86d330..2d94ce78e31 100644 --- a/packages/vuetify/src/components/VList/VListGroup.tsx +++ b/packages/vuetify/src/components/VList/VListGroup.tsx @@ -66,6 +66,7 @@ export const VListGroup = genericComponent()({ const { isBooted } = useSsrBoot() function onClick (e: Event) { + e.stopPropagation() open(!isOpen.value, e) }