Changes to UI and other stuff
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user