22import { h , resolveComponent } from ' vue'
33import { upperFirst } from ' scule'
44import type { TableColumn , TableRow } from ' @nuxt/ui'
5+ import type { Column } from ' @tanstack/vue-table'
56import { getPaginationRowModel } from ' @tanstack/vue-table'
67import { useClipboard , refDebounced } from ' @vueuse/core'
78
@@ -17,6 +18,8 @@ type Payment = {
1718 id: string
1819 date: string
1920 status: ' paid' | ' failed' | ' refunded'
21+ firstName: string
22+ lastName: string
2023 email: string
2124 amount: number
2225}
@@ -25,137 +28,28 @@ const table = useTemplateRef('table')
2528
2629const virtualize = ref (false )
2730
28- const data = ref <Payment []>([{
29- id: ' 4600' ,
30- date: ' 2024-03-11T15:30:00' ,
31- status: ' paid' ,
32- 33- amount: 594
34- }, {
35- id: ' 4599' ,
36- date: ' 2024-03-11T10:10:00' ,
37- status: ' failed' ,
38- 39- amount: 276
40- }, {
41- id: ' 4598' ,
42- date: ' 2024-03-11T08:50:00' ,
43- status: ' refunded' ,
44- 45- amount: 315
46- }, {
47- id: ' 4597' ,
48- date: ' 2024-03-10T19:45:00' ,
49- status: ' paid' ,
50- 51- amount: 529
52- }, {
53- id: ' 4596' ,
54- date: ' 2024-03-10T15:55:00' ,
55- status: ' paid' ,
56- 57- amount: 639
58- }, {
59- id: ' 4595' ,
60- date: ' 2024-03-10T13:40:00' ,
61- status: ' refunded' ,
62- 63- amount: 428
64- }, {
65- id: ' 4594' ,
66- date: ' 2024-03-10T09:15:00' ,
67- status: ' paid' ,
68- 69- amount: 683
70- }, {
71- id: ' 4593' ,
72- date: ' 2024-03-09T20:25:00' ,
73- status: ' failed' ,
74- 75- amount: 947
76- }, {
77- id: ' 4592' ,
78- date: ' 2024-03-09T18:45:00' ,
79- status: ' paid' ,
80- 81- amount: 851
82- }, {
83- id: ' 4591' ,
84- date: ' 2024-03-09T16:05:00' ,
85- status: ' paid' ,
86- 87- amount: 762
88- }, {
89- id: ' 4590' ,
90- date: ' 2024-03-09T14:20:00' ,
91- status: ' paid' ,
92- 93- amount: 573
94- }, {
95- id: ' 4589' ,
96- date: ' 2024-03-09T11:35:00' ,
97- status: ' failed' ,
98- 99- amount: 389
100- }, {
101- id: ' 4588' ,
102- date: ' 2024-03-08T22:50:00' ,
103- status: ' refunded' ,
104- 105- amount: 701
106- }, {
107- id: ' 4587' ,
108- date: ' 2024-03-08T20:15:00' ,
109- status: ' paid' ,
110- 111- amount: 856
112- }, {
113- id: ' 4586' ,
114- date: ' 2024-03-08T17:40:00' ,
115- status: ' paid' ,
116- 117- amount: 492
118- }, {
119- id: ' 4585' ,
120- date: ' 2024-03-08T14:55:00' ,
121- status: ' failed' ,
122- 123- amount: 637
124- }, {
125- id: ' 4584' ,
126- date: ' 2024-03-08T12:30:00' ,
127- status: ' paid' ,
128- 129- amount: 784
130- }, {
131- id: ' 4583' ,
132- date: ' 2024-03-08T09:45:00' ,
133- status: ' refunded' ,
134- 135- amount: 345
136- }, {
137- id: ' 4582' ,
138- date: ' 2024-03-07T23:10:00' ,
139- status: ' paid' ,
140- 141- amount: 918
142- }, {
143- id: ' 4581' ,
144- date: ' 2024-03-07T20:25:00' ,
145- status: ' paid' ,
146- 147- amount: 567
148- }])
149-
150- const largeData = useState <Payment []>(' largeData' , () => Array .from ({ length: 1000 }, (_ , i ) => ({
151- id: ` 4580-${i } ` ,
152- date: new Date ().toISOString (),
153- status: ' paid' ,
154- email: ` email-${i }@example.com ` ,
155- amount: Math .random () * 1000
156- })))
157-
158- const currentID = ref (4601 )
31+ const statuses: Payment [' status' ][] = [' paid' , ' failed' , ' refunded' ]
32+ const domains = [' gmail.com' , ' outlook.com' , ' yahoo.com' , ' company.com' , ' mail.com' ]
33+ const firstNames = [' john' , ' jane' , ' alex' , ' sarah' , ' mike' , ' emma' , ' david' , ' lisa' , ' chris' , ' anna' ]
34+ const lastNames = [' smith' , ' johnson' , ' williams' , ' brown' , ' jones' , ' garcia' , ' miller' , ' davis' , ' rodriguez' , ' martinez' ]
35+
36+ function makeData(id : number | string , index ? : number ): Payment {
37+ const i = index ?? Number (id )
38+ const firstName = firstNames [i % firstNames .length ]!
39+ const lastName = lastNames [i % lastNames .length ]!
40+
41+ return {
42+ id: id .toString (),
43+ date: index !== undefined ? new Date (Date .now () - index * 3600000 * 2 ).toISOString () : new Date ().toISOString (),
44+ firstName ,
45+ lastName ,
46+ status: statuses [i % statuses .length ]! ,
47+ email: ` ${firstName }.${lastName }${i > 100 ? Math .floor (i / 10 ) : ' ' }@${domains [i % domains .length ]} ` ,
48+ amount: Math .floor (Math .random () * 900 ) + 100
49+ }
50+ }
51+
52+ const data = useState <Payment []>(' data' , () => Array .from ({ length: 1000 }, (_ , i ) => makeData (45800 - i , i )))
15953
16054function getRowItems(row : TableRow <Payment >) {
16155 return [{
@@ -199,11 +93,13 @@ const columns: TableColumn<Payment>[] = [{
19993 ' aria-label' : ' Select row'
20094 }),
20195 enableSorting: false ,
202- enableHiding: false
96+ enableHiding: false ,
97+ size: 32
20398}, {
20499 accessorKey: ' id' ,
205- header: ' #' ,
206- cell : ({ row }) => ` #${row .getValue (' id' )} `
100+ header : ({ column }) => getPinnedHeader (column , ' #' , ' left' ),
101+ cell : ({ row }) => ` #${row .getValue (' id' )} ` ,
102+ size: 84
207103}, {
208104 accessorKey: ' date' ,
209105 header: ' Date' ,
@@ -224,7 +120,7 @@ const columns: TableColumn<Payment>[] = [{
224120 }
225121}, {
226122 accessorKey: ' status' ,
227- header: ' Status' ,
123+ header : ({ column }) => getPinnedHeader ( column , ' Status' , ' left ' ) ,
228124 cell : ({ row }) => {
229125 const color = ({
230126 paid: ' success' as const ,
@@ -233,7 +129,18 @@ const columns: TableColumn<Payment>[] = [{
233129 })[row .getValue (' status' ) as string ]
234130
235131 return h (UBadge , { class: ' capitalize' , variant: ' subtle' , color }, () => row .getValue (' status' ))
236- }
132+ },
133+ size: 102
134+ }, {
135+ accessorKey: ' firstName' ,
136+ header : ({ column }) => getPinnedHeader (column , ' First Name' , ' left' ),
137+ cell : ({ row }) => h (' div' , { class: ' capitalize' }, row .getValue (' firstName' )),
138+ size: 128
139+ }, {
140+ accessorKey: ' lastName' ,
141+ header : ({ column }) => getPinnedHeader (column , ' Last Name' , ' left' ),
142+ cell : ({ row }) => h (' div' , { class: ' capitalize' }, row .getValue (' lastName' )),
143+ size: 128
237144}, {
238145 accessorKey: ' email' ,
239146 header : ({ column }) => {
@@ -251,7 +158,7 @@ const columns: TableColumn<Payment>[] = [{
251158 cell : ({ row }) => h (' div' , { class: ' lowercase' }, row .getValue (' email' ))
252159}, {
253160 accessorKey: ' amount' ,
254- header : () => h (' div' , { class: ' text-right' }, ' Amount' ),
161+ header : ({ column } ) => h (' div' , { class: ' text-right' }, getPinnedHeader ( column , ' Amount' , ' right ' ) ),
255162 footer : ({ column }) => {
256163 const total = column .getFacetedRowModel ().rows .reduce ((acc : number , row : TableRow <Payment >) => acc + Number .parseFloat (row .getValue (' amount' )), 0 )
257164
@@ -271,7 +178,8 @@ const columns: TableColumn<Payment>[] = [{
271178 }).format (amount )
272179
273180 return h (' div' , { class: ' text-right font-medium' }, formatted )
274- }
181+ },
182+ size: 117
275183}, {
276184 id: ' actions' ,
277185 enableHiding: false ,
@@ -289,33 +197,43 @@ const columns: TableColumn<Payment>[] = [{
289197 ' class' : ' ms-auto' ,
290198 ' aria-label' : ' Actions dropdown'
291199 })))
292- }
200+ },
201+ size: 64
293202}]
294203
204+ function getPinnedHeader(column : Column <Payment >, label : string , position : ' left' | ' right' ) {
205+ const isPinned = column .getIsPinned ()
206+
207+ return h (UButton , {
208+ color: ' neutral' ,
209+ variant: ' ghost' ,
210+ label ,
211+ icon: isPinned ? ' i-lucide-pin-off' : ' i-lucide-pin' ,
212+ class: ' -mx-2.5' ,
213+ onClick() {
214+ column .pin (isPinned === position ? false : position )
215+ }
216+ })
217+ }
218+
295219const loading = ref (true )
296220const columnPinning = ref ({
297- left: [' id ' ],
221+ left: [' select ' ],
298222 right: [' actions' ]
299223})
300224
301225const pagination = ref ({
302226 pageIndex: 0 ,
303- pageSize: 10
227+ pageSize: 50
304228})
305229
306230function addElement() {
307- (virtualize .value ? largeData .value : data .value ).unshift ({
308- id: currentID .value .toString (),
309- date: new Date ().toISOString (),
310- status: ' paid' ,
311- 312- amount: Math .random () * 1000
313- })
314- currentID .value ++
231+ const maxId = Math .max (... data .value .map (item => Number (item .id )))
232+ data .value .unshift (makeData (maxId + 1 ))
315233}
316234
317235function randomize() {
318- ( virtualize . value ? largeData : data ). value = ( virtualize . value ? largeData : data ) .value .sort (() => Math .random () - 0.5 )
236+ data .value .sort (() => Math .random () - 0.5 )
319237}
320238
321239const rowSelection = ref <Record <string , boolean >>({})
@@ -405,21 +323,17 @@ onMounted(() => {
405323 <UTable
406324 ref =" table"
407325 :key =" String(virtualize)"
326+ :data =" data"
408327 :columns =" columns"
409328 :column-pinning =" columnPinning"
410329 :row-selection =" rowSelection"
411330 :loading =" loading"
412331 :virtualize =" virtualize"
413- v-bind =" virtualize ? {
414- data: largeData
415- } : {
332+ v-bind =" virtualize ? {} : {
416333 data,
417334 pagination,
418335 paginationOptions: {
419336 getPaginationRowModel: getPaginationRowModel()
420- },
421- ui: {
422- tr: 'divide-x divide-default'
423337 }
424338 }"
425339 sticky
@@ -453,22 +367,13 @@ onMounted(() => {
453367 </div >
454368
455369 <div class =" flex items-center gap-1.5" >
456- <UButton
457- color =" neutral"
458- variant =" outline"
459- :disabled =" !table?.tableApi?.getCanPreviousPage()"
460- @click =" table?.tableApi?.previousPage()"
461- >
462- Prev
463- </UButton >
464- <UButton
465- color =" neutral"
466- variant =" outline"
467- :disabled =" !table?.tableApi?.getCanNextPage()"
468- @click =" table?.tableApi?.nextPage()"
469- >
470- Next
471- </UButton >
370+ <UPagination
371+ :disabled =" !!virtualize"
372+ :page =" (table?.tableApi?.getState().pagination.pageIndex ?? 0) + 1"
373+ :items-per-page =" table?.tableApi?.getState().pagination.pageSize ?? 10"
374+ :total =" table?.tableApi?.getFilteredRowModel().rows.length || 0"
375+ @update:page =" (p: number) => table?.tableApi?.setPageIndex(p - 1)"
376+ />
472377 </div >
473378 </div >
474379 </div >
0 commit comments