Skip to content

Commit

Permalink
feat(VTreeview): port open-on-click prop to v3 & enhancement (#20038)
Browse files Browse the repository at this point in the history
fixes #20009
fixes #20095
fixes #19414
fixes #20106
  • Loading branch information
yuwu9145 committed Jul 9, 2024
1 parent 4c03bff commit cb6b5ff
Show file tree
Hide file tree
Showing 17 changed files with 667 additions and 309 deletions.
2 changes: 1 addition & 1 deletion packages/api-generator/src/locale/en/VTreeview.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"returnObject": "When `true` will make `v-model`, `active.sync` and `open.sync` return the complete object instead of just the key.",
"rounded": "Provides an alternative active style for `v-treeview` node. Only visible when `activatable` is `true` and should not be used in conjunction with the `shaped` prop.",
"search": "The search model for filtering results.",
"selectable": "Will render a checkbox next to each node allowing them to be selected.",
"selectable": "Will render a checkbox next to each node allowing them to be selected. Additionally, the **[openOnClick](/api/v-treeview/#props-open-on-click)** property will be applied internally.",
"selectedColor": "The color of the selection checkbox.",
"selectionType": "Controls how the treeview selects nodes. There are two modes available: 'leaf' and 'independent'.",
"shaped": "Provides an alternative active style for `v-treeview` node. Only visible when `activatable` is `true` and should not be used in conjunction with the `rounded` prop.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
</v-sheet>
<v-card-text>
<v-treeview
v-model:open="open"
v-model:opened="open"
:filter="filter"
:items="items"
:search="search"
item-value="id"
open-all
>
<template v-slot:prepend="{ item }">
<v-icon
Expand Down
108 changes: 20 additions & 88 deletions packages/docs/src/examples/v-treeview/misc-selectable-icons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@
v-model:selected="tree"
:items="items"
:load-children="load"
expand-icon="mdi-chevron-down"
false-icon="mdi-bookmark-outline"
indeterminate-icon="mdi-bookmark-minus"
item-title="name"
item-value="name"
off-icon="mdi-bookmark-outline"
on-icon="mdi-bookmark"
selected-color="indigo"
open-on-click
item-value="id"
select-strategy="classic"
true-icon="mdi-bookmark"
return-object
selectable
></v-treeview>
Expand Down Expand Up @@ -54,7 +52,7 @@
<v-chip
v-for="(selection, i) in tree"
:key="i"
:text="selection"
:text="selection.name"
class="ma-1"
color="grey"
prepend-icon="mdi-beer"
Expand Down Expand Up @@ -87,23 +85,16 @@
</template>

<script setup>
import { computed, ref, watch } from 'vue'
import { ref, watch } from 'vue'
const breweries = ref([])
const tree = ref([])
const types = ref([])
const items = computed(() => {
const children = types.value.map(type => ({
id: type,
name: getName(type),
children: getChildren(type),
}))
return [{
id: 1,
name: 'All Breweries',
children,
}]
})
const items = ref([{
id: 1,
name: 'All Breweries',
children: [],
}])
function load () {
if (breweries.value.length) return
Expand Down Expand Up @@ -131,73 +122,14 @@
if (!acc.includes(type)) acc.push(type)
return acc
}, []).sort()
})
</script>

<script>
export default {
data: () => ({
breweries: [],
tree: [],
types: [],
}),
computed: {
items () {
const children = this.types.map(type => ({
id: type,
name: this.getName(type),
children: this.getChildren(type),
}))
return [{
id: 1,
name: 'All Breweries',
children,
}]
},
},
watch: {
breweries (val) {
this.types = val.reduce((acc, cur) => {
const type = cur.brewery_type
if (!acc.includes(type)) acc.push(type)
return acc
}, []).sort()
},
},
methods: {
load () {
if (this.breweries.length) return
return fetch('https://api.openbrewerydb.org/breweries')
.then(res => res.json())
.then(data => (this.breweries = data))
.catch(err => console.log(err))
},
getChildren (type) {
const breweries = []
for (const brewery of this.breweries) {
if (brewery.brewery_type !== type) continue
breweries.push({
...brewery,
name: this.getName(brewery.name),
})
}
return breweries.sort((a, b) => {
return a.name > b.name ? 1 : -1
})
},
getName (name) {
return `${name.charAt(0).toUpperCase()}${name.slice(1)}`
},
},
}
const children = types.value.map(type => ({
id: type,
name: getName(type),
children: getChildren(type),
}))
const rootObj = items.value[0]
rootObj.children = children
items.value = [rootObj]
})
</script>
1 change: 1 addition & 0 deletions packages/docs/src/examples/v-treeview/prop-activatable.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<v-treeview
:items="items"
item-value="id"
activatable
></v-treeview>
</template>
Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/examples/v-treeview/prop-color.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<v-treeview
:items="items"
color="warning"
item-value="id"
activatable
></v-treeview>
</template>
Expand Down
10 changes: 5 additions & 5 deletions packages/docs/src/examples/v-treeview/prop-selection-type.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
<v-container>
<v-select
v-model="selectionType"
:items="['leaf', 'independent']"
:items="['leaf', 'single-leaf', 'independent', 'single-independent', 'classic']"
label="Selection type"
></v-select>
<v-row>
<v-col>
<v-treeview
v-model="selection"
v-model:selected="selection"
:items="items"
:selection-type="selectionType"
open-all
:select-strategy="selectionType"
item-value="id"
return-object
selectable
></v-treeview>
Expand All @@ -29,7 +29,7 @@
v-for="node in selection"
:key="node.id"
>
{{ node.name }}
{{ node.title }}
</div>
</template>
</v-col>
Expand Down
79 changes: 3 additions & 76 deletions packages/docs/src/examples/v-treeview/slot-append-and-label.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<template>
<v-treeview
v-model="tree"
:items="items"
:opened="initiallyOpen"
item-key="name"
item-value="title"
activatable
open-on-click
>
<template v-slot:prepend="{ item, open }">
<template v-slot:prepend="{ item, isOpen }">
<v-icon v-if="!item.file">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
{{ isOpen ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.file] }}
Expand All @@ -32,7 +31,6 @@
txt: 'mdi-file-document-outline',
xls: 'mdi-file-excel',
})
const tree = ref([])
const items = ref([
{
title: '.git',
Expand Down Expand Up @@ -86,74 +84,3 @@
},
])
</script>

<script>
export default {
data: () => ({
initiallyOpen: ['public'],
files: {
html: 'mdi-language-html5',
js: 'mdi-nodejs',
json: 'mdi-code-json',
md: 'mdi-language-markdown',
pdf: 'mdi-file-pdf-box',
png: 'mdi-file-image',
txt: 'mdi-file-document-outline',
xls: 'mdi-file-excel',
},
tree: [],
items: [
{
title: '.git',
},
{
title: 'node_modules',
},
{
title: 'public',
children: [
{
title: 'static',
children: [{
title: 'logo.png',
file: 'png',
}],
},
{
title: 'favicon.ico',
file: 'png',
},
{
title: 'index.html',
file: 'html',
},
],
},
{
title: '.gitignore',
file: 'txt',
},
{
title: 'babel.config.js',
file: 'js',
},
{
title: 'package.json',
file: 'json',
},
{
title: 'README.md',
file: 'md',
},
{
title: 'vue.config.js',
file: 'js',
},
{
title: 'yarn.lock',
file: 'txt',
},
],
}),
}
</script>
4 changes: 2 additions & 2 deletions packages/docs/src/pages/en/components/treeview.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ Shaped treeview's have rounded borders on one side of the nodes.

### Slots

#### Append and label
#### Prepend

Using the the **label**, and an **append** slots we are able to create an intuitive file explorer.
Using the the **prepend** slot we are able to create an intuitive file explorer.

<ExamplesExample file="v-treeview/slot-append-and-label" />

Expand Down
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VList/VListItem.sass
Original file line number Diff line number Diff line change
Expand Up @@ -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--activetable-group-activator).v-list-item--active
.v-list-group__header:not(.v-treeview-item--activatable-group-activator).v-list-item--active
&:not(:focus-visible)
.v-list-item__overlay
opacity: 0
Expand Down
5 changes: 4 additions & 1 deletion packages/vuetify/src/components/VList/VListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type { RippleDirectiveBinding } from '@/directives/ripple'

export type ListItemSlot = {
isActive: boolean
isOpen: boolean
isSelected: boolean
isIndeterminate: boolean
select: (value: boolean) => void
Expand Down Expand Up @@ -86,7 +87,7 @@ export const makeVListItemProps = propsFactory({
title: [String, Number],
value: null,

onClick: EventProp<[MouseEvent]>(),
onClick: EventProp<[MouseEvent | KeyboardEvent]>(),
onClickOnce: EventProp<[MouseEvent]>(),

...makeBorderProps(),
Expand Down Expand Up @@ -119,6 +120,7 @@ export const VListItem = genericComponent<VListItemSlots>()({
activate,
isActivated,
select,
isOpen,
isSelected,
isIndeterminate,
isGroupActivator,
Expand Down Expand Up @@ -167,6 +169,7 @@ export const VListItem = genericComponent<VListItemSlots>()({
const slotProps = computed(() => ({
isActive: isActive.value,
select,
isOpen: isOpen.value,
isSelected: isSelected.value,
isIndeterminate: isIndeterminate.value,
} satisfies ListItemSlot))
Expand Down
6 changes: 3 additions & 3 deletions packages/vuetify/src/composables/nested/nested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const useNested = (props: NestedProps) => {
const children = ref(new Map<unknown, unknown[]>())
const parents = ref(new Map<unknown, unknown>())

const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(v), v => [...v.values()])
const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(toRaw(v)), v => [...v.values()])

const activeStrategy = computed(() => {
if (typeof props.activeStrategy === 'object') return props.activeStrategy
Expand Down Expand Up @@ -307,9 +307,9 @@ export const useNestedItem = (id: Ref<unknown>, isGroup: boolean) => {
const item = {
...parent,
id: computedId,
open: (open: boolean, e: Event) => parent.root.open(computedId.value, open, e),
open: (open: boolean, e: Event) => parent.root.open(toRaw(computedId.value), open, e),
openOnSelect: (open: boolean, e?: Event) => parent.root.openOnSelect(computedId.value, open, e),
isOpen: computed(() => parent.root.opened.value.has(computedId.value)),
isOpen: computed(() => parent.root.opened.value.has(toRaw(computedId.value))),
parent: computed(() => parent.root.parents.value.get(computedId.value)),
activate: (activated: boolean, e?: Event) => parent.root.activate(computedId.value, activated, e),
isActivated: computed(() => parent.root.activated.value.has(toRaw(computedId.value))),
Expand Down
Loading

0 comments on commit cb6b5ff

Please sign in to comment.