268 lines
7.0 KiB
JavaScript
268 lines
7.0 KiB
JavaScript
import { h } from 'vue';
|
|
import { Badge } from '@/Components/ui/badge';
|
|
import { Button } from '@/Components/ui/button';
|
|
import { Checkbox } from '@/Components/ui/checkbox';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/Components/ui/dropdown-menu';
|
|
import { MoreHorizontal, ArrowUpDown } from 'lucide-vue-next';
|
|
|
|
/**
|
|
* Example columns definition following shadcn-vue DataTable patterns
|
|
*
|
|
* Usage:
|
|
* import { columns } from './columns'
|
|
* <DataTable :columns="columns" :data="data" />
|
|
*
|
|
* This is a TypeScript-like example for JavaScript.
|
|
* The columns follow TanStack Table's ColumnDef format.
|
|
*/
|
|
|
|
/**
|
|
* Simple format - automatically converted to ColumnDef
|
|
* Use this for basic tables
|
|
*/
|
|
export const simpleColumns = [
|
|
{ key: 'id', label: 'ID', sortable: true },
|
|
{ key: 'name', label: 'Name', sortable: true },
|
|
{ key: 'email', label: 'Email', sortable: true },
|
|
{ key: 'status', label: 'Status', sortable: false },
|
|
];
|
|
|
|
/**
|
|
* Advanced format - full TanStack Table ColumnDef
|
|
* Use this for custom rendering, formatting, etc.
|
|
*/
|
|
export const advancedColumns = [
|
|
// Selection column (added automatically if enableRowSelection prop is true)
|
|
// {
|
|
// id: 'select',
|
|
// header: ({ table }) => {
|
|
// return h(Checkbox, {
|
|
// modelValue: table.getIsAllPageRowsSelected(),
|
|
// indeterminate: table.getIsSomePageRowsSelected(),
|
|
// 'onUpdate:modelValue': (value) => table.toggleAllPageRowsSelected(!!value),
|
|
// 'aria-label': 'Select all',
|
|
// });
|
|
// },
|
|
// cell: ({ row }) => {
|
|
// return h(Checkbox, {
|
|
// modelValue: row.getIsSelected(),
|
|
// 'onUpdate:modelValue': (value) => row.toggleSelected(!!value),
|
|
// 'aria-label': 'Select row',
|
|
// });
|
|
// },
|
|
// enableSorting: false,
|
|
// enableHiding: false,
|
|
// },
|
|
|
|
// ID column
|
|
{
|
|
accessorKey: 'id',
|
|
header: ({ column }) => {
|
|
return h(
|
|
Button,
|
|
{
|
|
variant: 'ghost',
|
|
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
|
},
|
|
() => ['ID', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })]
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
return h('div', { class: 'w-20 font-medium' }, row.getValue('id'));
|
|
},
|
|
},
|
|
|
|
// Name column
|
|
{
|
|
accessorKey: 'name',
|
|
header: 'Name',
|
|
cell: ({ row }) => {
|
|
return h('div', { class: 'font-medium' }, row.getValue('name'));
|
|
},
|
|
},
|
|
|
|
// Email column with custom rendering
|
|
{
|
|
accessorKey: 'email',
|
|
header: ({ column }) => {
|
|
return h(
|
|
Button,
|
|
{
|
|
variant: 'ghost',
|
|
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
|
},
|
|
() => ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })]
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
return h('div', { class: 'lowercase' }, row.getValue('email'));
|
|
},
|
|
},
|
|
|
|
// Amount column with formatting
|
|
{
|
|
accessorKey: 'amount',
|
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
|
cell: ({ row }) => {
|
|
const amount = parseFloat(row.getValue('amount'));
|
|
const formatted = new Intl.NumberFormat('sl-SI', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
}).format(amount);
|
|
|
|
return h('div', { class: 'text-right font-medium' }, formatted);
|
|
},
|
|
},
|
|
|
|
// Status column with badge
|
|
{
|
|
accessorKey: 'status',
|
|
header: 'Status',
|
|
cell: ({ row }) => {
|
|
const status = row.getValue('status');
|
|
const variants = {
|
|
success: 'default',
|
|
pending: 'secondary',
|
|
failed: 'destructive',
|
|
};
|
|
|
|
return h(
|
|
Badge,
|
|
{
|
|
variant: variants[status] || 'outline',
|
|
},
|
|
() => status
|
|
);
|
|
},
|
|
},
|
|
|
|
// Actions column
|
|
{
|
|
id: 'actions',
|
|
enableHiding: false,
|
|
cell: ({ row }) => {
|
|
const item = row.original;
|
|
|
|
return h(
|
|
'div',
|
|
{ class: 'text-right' },
|
|
h(
|
|
DropdownMenu,
|
|
{},
|
|
{
|
|
default: () => [
|
|
h(
|
|
DropdownMenuTrigger,
|
|
{ asChild: true },
|
|
{
|
|
default: () =>
|
|
h(
|
|
Button,
|
|
{
|
|
variant: 'ghost',
|
|
class: 'h-8 w-8 p-0',
|
|
},
|
|
{
|
|
default: () => [
|
|
h('span', { class: 'sr-only' }, 'Open menu'),
|
|
h(MoreHorizontal, { class: 'h-4 w-4' }),
|
|
],
|
|
}
|
|
),
|
|
}
|
|
),
|
|
h(
|
|
DropdownMenuContent,
|
|
{ align: 'end' },
|
|
{
|
|
default: () => [
|
|
h(DropdownMenuLabel, {}, () => 'Actions'),
|
|
h(
|
|
DropdownMenuItem,
|
|
{
|
|
onClick: () => navigator.clipboard.writeText(item.id),
|
|
},
|
|
() => 'Copy ID'
|
|
),
|
|
h(DropdownMenuSeparator),
|
|
h(DropdownMenuItem, {}, () => 'View details'),
|
|
h(DropdownMenuItem, {}, () => 'Edit'),
|
|
],
|
|
}
|
|
),
|
|
],
|
|
}
|
|
)
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Payments example from shadcn-vue docs
|
|
*/
|
|
export const paymentColumns = [
|
|
{
|
|
accessorKey: 'status',
|
|
header: 'Status',
|
|
cell: ({ row }) => {
|
|
const status = row.getValue('status');
|
|
return h('div', { class: 'capitalize' }, status);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'email',
|
|
header: ({ column }) => {
|
|
return h(
|
|
Button,
|
|
{
|
|
variant: 'ghost',
|
|
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
|
|
},
|
|
() => ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })]
|
|
);
|
|
},
|
|
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
|
|
},
|
|
{
|
|
accessorKey: 'amount',
|
|
header: () => h('div', { class: 'text-right' }, 'Amount'),
|
|
cell: ({ row }) => {
|
|
const amount = parseFloat(row.getValue('amount'));
|
|
const formatted = new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
}).format(amount);
|
|
return h('div', { class: 'text-right font-medium' }, formatted);
|
|
},
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Example with custom cell slots
|
|
* Use template slots in your component:
|
|
*
|
|
* <DataTable :columns="columnsWithSlots" :data="data">
|
|
* <template #cell-status="{ value }">
|
|
* <Badge :variant="value === 'active' ? 'default' : 'secondary'">
|
|
* {{ value }}
|
|
* </Badge>
|
|
* </template>
|
|
* </DataTable>
|
|
*/
|
|
export const columnsWithSlots = [
|
|
{ key: 'id', label: 'ID', sortable: true },
|
|
{ key: 'name', label: 'Name', sortable: true },
|
|
{ key: 'status', label: 'Status', sortable: false }, // Will use #cell-status slot
|
|
{ key: 'email', label: 'Email', sortable: true },
|
|
];
|
|
|
|
export default advancedColumns;
|