From d13e68fc45cf7c03c3d2b4f4b4cd1415a9c60526 Mon Sep 17 00:00:00 2001 From: Yuchao Wu Date: Sat, 11 May 2024 11:50:41 +1000 Subject: [PATCH] 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;