Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(VTreeview): port open-on-click prop to v3 & enhancement #20038

Merged
merged 14 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading