Skip to content

Commit 355d66c

Browse files
docs(table): add example with tree data (#4795)
Co-authored-by: Benjamin Canac <[email protected]>
1 parent c00bf30 commit 355d66c

File tree

2 files changed

+183
-1
lines changed

2 files changed

+183
-1
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<script setup lang="ts">
2+
import { h, resolveComponent } from 'vue'
3+
import type { TableColumn } from '@nuxt/ui'
4+
5+
const UCheckbox = resolveComponent('UCheckbox')
6+
const UButton = resolveComponent('UButton')
7+
8+
type Payment = {
9+
id: string
10+
date: string
11+
email: string
12+
amount: number
13+
children?: Payment[]
14+
}
15+
16+
const data = ref<Payment[]>([{
17+
id: '4600',
18+
date: '2024-03-11T15:30:00',
19+
20+
amount: 594,
21+
children: [
22+
{
23+
id: '4599',
24+
date: '2024-03-11T10:10:00',
25+
26+
amount: 276
27+
}, {
28+
id: '4598',
29+
date: '2024-03-11T08:50:00',
30+
31+
amount: 315
32+
}, {
33+
id: '4597',
34+
date: '2024-03-10T19:45:00',
35+
36+
amount: 529,
37+
children: [
38+
{
39+
id: '4592',
40+
date: '2024-03-09T18:45:00',
41+
42+
amount: 851
43+
}, {
44+
id: '4591',
45+
date: '2024-03-09T16:05:00',
46+
47+
amount: 762
48+
}, {
49+
id: '4590',
50+
date: '2024-03-09T14:20:00',
51+
52+
amount: 573,
53+
children: [
54+
{
55+
id: '4596',
56+
date: '2024-03-10T15:55:00',
57+
58+
amount: 639
59+
}, {
60+
id: '4595',
61+
date: '2024-03-10T13:40:00',
62+
63+
amount: 428
64+
}
65+
]
66+
}
67+
]
68+
}
69+
]
70+
}, {
71+
id: '4589',
72+
date: '2024-03-09T11:35:00',
73+
74+
amount: 389
75+
}])
76+
77+
const columns: TableColumn<Payment>[] = [{
78+
id: 'select',
79+
header: ({ table }) => h(UCheckbox, {
80+
'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(),
81+
'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
82+
'aria-label': 'Select all'
83+
}),
84+
cell: ({ row }) => h(UCheckbox, {
85+
'modelValue': row.getIsSelected() ? true : row.getIsSomeSelected() ? 'indeterminate' : false,
86+
'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
87+
'aria-label': 'Select row'
88+
})
89+
}, {
90+
accessorKey: 'id',
91+
header: '#',
92+
cell: ({ row }) => {
93+
return h(
94+
'div',
95+
{
96+
style: {
97+
paddingLeft: `${row.depth}rem`
98+
},
99+
class: 'flex items-center gap-2'
100+
},
101+
[
102+
h(UButton, {
103+
color: 'neutral',
104+
variant: 'outline',
105+
size: 'xs',
106+
icon: row.getIsExpanded() ? 'i-lucide-minus' : 'i-lucide-plus',
107+
class: !row.getCanExpand() && 'invisible',
108+
ui: {
109+
base: 'p-0 rounded-sm',
110+
leadingIcon: 'size-4'
111+
},
112+
onClick: row.getToggleExpandedHandler()
113+
}),
114+
row.getValue('id') as string
115+
]
116+
)
117+
}
118+
}, {
119+
accessorKey: 'date',
120+
header: 'Date',
121+
cell: ({ row }) => {
122+
return new Date(row.getValue('date')).toLocaleString('en-US', {
123+
day: 'numeric',
124+
month: 'short',
125+
hour: '2-digit',
126+
minute: '2-digit',
127+
hour12: false
128+
})
129+
}
130+
}, {
131+
accessorKey: 'email',
132+
header: 'Email'
133+
}, {
134+
accessorKey: 'amount',
135+
header: () => h('div', { class: 'text-right' }, 'Amount'),
136+
cell: ({ row }) => {
137+
const amount = Number.parseFloat(row.getValue('amount'))
138+
139+
const formatted = new Intl.NumberFormat('en-US', {
140+
style: 'currency',
141+
currency: 'EUR'
142+
}).format(amount)
143+
144+
return h('div', { class: 'text-right font-medium' }, formatted)
145+
}
146+
}]
147+
148+
const expanded = ref({ 0: true })
149+
</script>
150+
151+
<template>
152+
<UTable
153+
v-model:expanded="expanded"
154+
:data="data"
155+
:columns="columns"
156+
:get-sub-rows="row => row.children"
157+
sticky
158+
class="flex-1"
159+
:ui="{
160+
base: 'border-separate border-spacing-0',
161+
tbody: '[&>tr]:last:[&>td]:border-b-0',
162+
tr: 'group',
163+
td: 'empty:p-0 group-has-[td:not(:empty)]:border-b border-default'
164+
}"
165+
/>
166+
</template>

docs/content/docs/2.components/table.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ class: '!p-0'
574574

575575
### With drag and drop
576576

577-
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the Table. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
577+
You can use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the Table. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.
578578

579579
::note
580580
Since the table ref doesn't expose the tbody element, add a unique class to it via the `:ui` prop to target it with `useSortable` (e.g. `:ui="{ tbody: 'my-table-tbody' }"`).
@@ -589,6 +589,22 @@ class: '!p-0'
589589
---
590590
::
591591

592+
### With tree data
593+
594+
You can use the `get-sub-rows` prop to display hierarchical (tree) data in the table.
595+
For example, if your data objects have a `children` array, set `:get-sub-rows="row => row.children"` to enable expandable rows.
596+
597+
::component-example
598+
---
599+
prettier: true
600+
collapse: true
601+
highlights:
602+
- 168
603+
name: 'table-tree-data-example'
604+
class: '!p-0'
605+
---
606+
::
607+
592608
### With slots
593609

594610
You can use slots to customize the header and data cells of the table.

0 commit comments

Comments
 (0)