I'm trying Laravel 12 with the new starter kit Vue and the shadcn-vue
components. Here is my problem: I need a reactive datatable using Inertia. To achieve that reactivity I have to disable preserveState
, however, by disabling it get lost the arrow icons indicating the active sort ordering on the header column table. If preserveState
is enabled (true) then the arrow icons works fine but reactivity on data is lost despite the Inertia's request still works with the data right sorted.
Here is what I've done so far:
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import AppLayout from '@/layouts/AppLayout.vue';
import IndexLayout from '@/layouts/IndexLayout.vue';
import { valueUpdater } from '@/lib/utils';
import { BreadcrumbItem, Can, Pagination, Permission } from '@/types';
import { Head, router } from '@inertiajs/vue3';
import {
ColumnDef,
ColumnFiltersState,
ExpandedState,
FlexRender,
getCoreRowModel,
getFilteredRowModel,
SortingState,
useVueTable,
} from '@tanstack/vue-table';
import { ChevronDown, ChevronsUpDown, ChevronUp } from 'lucide-vue-next';
import { h, ref } from 'vue';
import DropdownAction from './partials/DropdownAction.vue';
interface Props {
can: Can;
filters: object;
permissions: Pagination<Permission>;
}
const props = defineProps<Props>();
// const model = defineModel<Props>();
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Permisos',
href: '/dashboard',
},
];
const columns: ColumnDef<Permission>[] = [
{
id: 'select',
header: ({ table }) =>
h(Checkbox, {
modelValue: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
'onUpdate:modelValue': (value: any) => table.toggleAllPageRowsSelected(!!value),
ariaLabel: 'Select all',
}),
cell: ({ row }) =>
h(Checkbox, {
modelValue: row.getIsSelected(),
'onUpdate:modelValue': (value: any) => row.toggleSelected(!!value),
ariaLabel: 'Select row',
}),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'name',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Nombre',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('name')),
},
{
accessorKey: 'description',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Descripción',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('description')),
},
{
id: 'actions',
enableHiding: false,
cell: ({ row }) => {
const permission = row.original;
const can = props.can;
return h(DropdownAction, {
permission,
can,
onExpand: row.toggleExpanded,
});
},
},
];
const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]);
const globalFilter = ref('');
const rowSelection = ref({});
const expanded = ref<ExpandedState>({});
function handleSortingChange(item: any) {
if (typeof item === 'function') {
const sortValue = item();
const sortBy = sortValue[0]?.id ? sortValue[0].id : '' ;
const sortDirection = sortBy ? sortValue[0]?.desc ? 'desc' : 'asc' : '';
const data: {[index: string]: any} = {};
data[sortBy] = sortDirection;
router.visit(route('permissions.index'), {
data,
only: ['permissions'],
preserveScroll: true,
preserveState: true,
onSuccess: (page) => {
console.log(page);
sorting.value = sortValue;
// model.value = page.props.permissions.data;
}
});
}
}
const table = useVueTable({
data: props.permissions.data,
columns,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
manualSorting: true,
enableSortingRemoval: true,
pageCount: props.permissions.per_page,
getFilteredRowModel: getFilteredRowModel(),
onSortingChange: (updaterOrValue) => handleSortingChange(updaterOrValue),
onColumnFiltersChange: (updaterOrValue) => valueUpdater(updaterOrValue, columnFilters),
onGlobalFilterChange: (updaterOrValue) => valueUpdater(updaterOrValue, globalFilter),
onRowSelectionChange: (updaterOrValue) => valueUpdater(updaterOrValue, rowSelection),
state: {
get sorting() {
return sorting.value;
},
get columnFilters() {
return columnFilters.value;
},
get globalFilter() {
return globalFilter.value;
},
get rowSelection() {
return rowSelection.value;
},
get expanded() {
return expanded.value;
},
},
});
</script>
<template>
<AppLayout :breadcrumbs="breadcrumbs">
<Head title="Permisos" />
<IndexLayout title="Permisos">
<div class="w-full">
<div class="flex items-center py-4">
<Input
class="max-w-sm"
placeholder="Filter emails..."
:model-value="globalFilter ?? ''"
@update:model-value="(value) => (globalFilter = String(value))"
/>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline" class="ml-auto"> Columns <ChevronDown class="ml-2 h-4 w-4" /> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuCheckboxItem
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
:key="column.id"
class="capitalize"
:model-value="column.getIsVisible()"
@update:model-value="
(value: any) => {
column.toggleVisibility(!!value);
}
"
>
{{ column.id }}
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div class="rounded-md border">
<Table>
<TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<TableHead v-for="header in headerGroup.headers" :key="header.id">
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<template v-for="row in table.getRowModel().rows" :key="row.id">
<TableRow :data-state="row.getIsSelected() && 'selected'">
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell>
</TableRow>
<TableRow v-if="row.getIsExpanded()">
<TableCell :colspan="row.getAllCells().length">
{{ JSON.stringify(row.original) }}
</TableCell>
</TableRow>
</template>
</template>
<TableRow v-else>
<TableCell :colspan="columns.length" class="h-24 text-center"> No results. </TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<div class="flex items-center justify-end space-x-2 py-4">
<div class="flex-1 text-sm text-muted-foreground">
{{ table.getFilteredSelectedRowModel().rows.length }} de {{ table.getFilteredRowModel().rows.length }} fila(s) seleccionadas.
</div>
<div class="space-x-2">
<Button variant="outline" size="sm" :disabled="!table.getCanPreviousPage()" @click="table.previousPage()"> Anterior </Button>
<Button variant="outline" size="sm" :disabled="!table.getCanNextPage()" @click="table.nextPage()"> Siguiente </Button>
</div>
</div>
</div>
</IndexLayout>
</AppLayout>
</template>
The official documentation says that defineModel
should be used to mutate the data, but still I didn't get it to work properly.
Please, any help will be very appreciate it. Thanks in advance.
I'm trying Laravel 12 with the new starter kit Vue and the shadcn-vue
components. Here is my problem: I need a reactive datatable using Inertia. To achieve that reactivity I have to disable preserveState
, however, by disabling it get lost the arrow icons indicating the active sort ordering on the header column table. If preserveState
is enabled (true) then the arrow icons works fine but reactivity on data is lost despite the Inertia's request still works with the data right sorted.
Here is what I've done so far:
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import AppLayout from '@/layouts/AppLayout.vue';
import IndexLayout from '@/layouts/IndexLayout.vue';
import { valueUpdater } from '@/lib/utils';
import { BreadcrumbItem, Can, Pagination, Permission } from '@/types';
import { Head, router } from '@inertiajs/vue3';
import {
ColumnDef,
ColumnFiltersState,
ExpandedState,
FlexRender,
getCoreRowModel,
getFilteredRowModel,
SortingState,
useVueTable,
} from '@tanstack/vue-table';
import { ChevronDown, ChevronsUpDown, ChevronUp } from 'lucide-vue-next';
import { h, ref } from 'vue';
import DropdownAction from './partials/DropdownAction.vue';
interface Props {
can: Can;
filters: object;
permissions: Pagination<Permission>;
}
const props = defineProps<Props>();
// const model = defineModel<Props>();
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Permisos',
href: '/dashboard',
},
];
const columns: ColumnDef<Permission>[] = [
{
id: 'select',
header: ({ table }) =>
h(Checkbox, {
modelValue: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
'onUpdate:modelValue': (value: any) => table.toggleAllPageRowsSelected(!!value),
ariaLabel: 'Select all',
}),
cell: ({ row }) =>
h(Checkbox, {
modelValue: row.getIsSelected(),
'onUpdate:modelValue': (value: any) => row.toggleSelected(!!value),
ariaLabel: 'Select row',
}),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'name',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Nombre',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('name')),
},
{
accessorKey: 'description',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Descripción',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('description')),
},
{
id: 'actions',
enableHiding: false,
cell: ({ row }) => {
const permission = row.original;
const can = props.can;
return h(DropdownAction, {
permission,
can,
onExpand: row.toggleExpanded,
});
},
},
];
const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]);
const globalFilter = ref('');
const rowSelection = ref({});
const expanded = ref<ExpandedState>({});
function handleSortingChange(item: any) {
if (typeof item === 'function') {
const sortValue = item();
const sortBy = sortValue[0]?.id ? sortValue[0].id : '' ;
const sortDirection = sortBy ? sortValue[0]?.desc ? 'desc' : 'asc' : '';
const data: {[index: string]: any} = {};
data[sortBy] = sortDirection;
router.visit(route('permissions.index'), {
data,
only: ['permissions'],
preserveScroll: true,
preserveState: true,
onSuccess: (page) => {
console.log(page);
sorting.value = sortValue;
// model.value = page.props.permissions.data;
}
});
}
}
const table = useVueTable({
data: props.permissions.data,
columns,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
manualSorting: true,
enableSortingRemoval: true,
pageCount: props.permissions.per_page,
getFilteredRowModel: getFilteredRowModel(),
onSortingChange: (updaterOrValue) => handleSortingChange(updaterOrValue),
onColumnFiltersChange: (updaterOrValue) => valueUpdater(updaterOrValue, columnFilters),
onGlobalFilterChange: (updaterOrValue) => valueUpdater(updaterOrValue, globalFilter),
onRowSelectionChange: (updaterOrValue) => valueUpdater(updaterOrValue, rowSelection),
state: {
get sorting() {
return sorting.value;
},
get columnFilters() {
return columnFilters.value;
},
get globalFilter() {
return globalFilter.value;
},
get rowSelection() {
return rowSelection.value;
},
get expanded() {
return expanded.value;
},
},
});
</script>
<template>
<AppLayout :breadcrumbs="breadcrumbs">
<Head title="Permisos" />
<IndexLayout title="Permisos">
<div class="w-full">
<div class="flex items-center py-4">
<Input
class="max-w-sm"
placeholder="Filter emails..."
:model-value="globalFilter ?? ''"
@update:model-value="(value) => (globalFilter = String(value))"
/>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline" class="ml-auto"> Columns <ChevronDown class="ml-2 h-4 w-4" /> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuCheckboxItem
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
:key="column.id"
class="capitalize"
:model-value="column.getIsVisible()"
@update:model-value="
(value: any) => {
column.toggleVisibility(!!value);
}
"
>
{{ column.id }}
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div class="rounded-md border">
<Table>
<TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<TableHead v-for="header in headerGroup.headers" :key="header.id">
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<template v-for="row in table.getRowModel().rows" :key="row.id">
<TableRow :data-state="row.getIsSelected() && 'selected'">
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell>
</TableRow>
<TableRow v-if="row.getIsExpanded()">
<TableCell :colspan="row.getAllCells().length">
{{ JSON.stringify(row.original) }}
</TableCell>
</TableRow>
</template>
</template>
<TableRow v-else>
<TableCell :colspan="columns.length" class="h-24 text-center"> No results. </TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<div class="flex items-center justify-end space-x-2 py-4">
<div class="flex-1 text-sm text-muted-foreground">
{{ table.getFilteredSelectedRowModel().rows.length }} de {{ table.getFilteredRowModel().rows.length }} fila(s) seleccionadas.
</div>
<div class="space-x-2">
<Button variant="outline" size="sm" :disabled="!table.getCanPreviousPage()" @click="table.previousPage()"> Anterior </Button>
<Button variant="outline" size="sm" :disabled="!table.getCanNextPage()" @click="table.nextPage()"> Siguiente </Button>
</div>
</div>
</div>
</IndexLayout>
</AppLayout>
</template>
The official documentation says that defineModel
should be used to mutate the data, but still I didn't get it to work properly.
Please, any help will be very appreciate it. Thanks in advance.
Share Improve this question asked Mar 14 at 3:13 DovahkiinDovahkiin 481 silver badge7 bronze badges1 Answer
Reset to default 0Well it seems that I got myself the answer to my problem: I disabled the manualSorting
property and set back getSortedRowModel
to getSortedRowModel: getSortedRowModel()
and on onSortingChange
property I kept my manual sorting handle function and... It worked!!! Arrow icons (well chevron icons) works and the sorting from the backend provided by the Inertia's request works too.
By reading the docs (https://tanstack/table/v8/docs/guide/sorting#client-side-vs-server-side-sorting) I thought that have to set the manual sorting mode on true in order to achieve the sorting from backend... Anyway here are the changes I made to solve this...
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import AppLayout from '@/layouts/AppLayout.vue';
import IndexLayout from '@/layouts/IndexLayout.vue';
import { valueUpdater } from '@/lib/utils';
import { BreadcrumbItem, Can, Pagination, Permission } from '@/types';
import { Head, router } from '@inertiajs/vue3';
import {
ColumnDef,
ColumnFiltersState,
ExpandedState,
FlexRender,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
SortingState,
useVueTable,
} from '@tanstack/vue-table';
import { ChevronDown, ChevronsUpDown, ChevronUp } from 'lucide-vue-next';
import { h, ref } from 'vue';
import DropdownAction from './partials/DropdownAction.vue';
interface Props {
can: Can;
filters: object;
permissions: Pagination<Permission>;
}
const props = defineProps<Props>();
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Permisos',
href: '/dashboard',
},
];
const columns: ColumnDef<Permission>[] = [
{
id: 'select',
header: ({ table }) =>
h(Checkbox, {
modelValue: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
'onUpdate:modelValue': (value: any) => table.toggleAllPageRowsSelected(!!value),
ariaLabel: 'Select all',
}),
cell: ({ row }) =>
h(Checkbox, {
modelValue: row.getIsSelected(),
'onUpdate:modelValue': (value: any) => row.toggleSelected(!!value),
ariaLabel: 'Select row',
}),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'name',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Nombre',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('name')),
},
{
accessorKey: 'description',
header: ({ column, table }) => {
const isSorted = column.getIsSorted();
const isSortedDesc = column.getIsSorted() === 'desc';
return h(DropdownMenu, () => [
h(DropdownMenuTrigger, { asChild: true }, () => [
h(Button, { variant: 'outline', class: 'ml-auto' }, () => [
'Descripción',
isSorted
? isSortedDesc
? h(ChevronDown, { class: 'ml-2 h-4 w-4' })
: h(ChevronUp, { class: 'ml-2 h-4 w-4' })
: h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' }),
]),
]),
h(DropdownMenuContent, { align: 'start' }, () => [
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Up`, checked: isSorted && !isSortedDesc, onSelect: () => column.toggleSorting(false) },
() => ['Ordenar ASC'],
),
h(
DropdownMenuCheckboxItem,
{ key: `${column.id}Down`, checked: isSorted && isSortedDesc, onSelect: () => column.toggleSorting(true) },
() => ['Ordenar DESC'],
),
h(DropdownMenuCheckboxItem, { key: `${column.id}Clr`, checked: false, onSelect: () => table.setSorting(() => <SortingState>([])) }, () => [
'Restablecer',
]),
]),
]);
},
cell: ({ row }) => h('div', row.getValue('description')),
},
{
id: 'actions',
enableHiding: false,
cell: ({ row }) => {
const permission = row.original;
const can = props.can;
return h(DropdownAction, {
permission,
can,
onExpand: row.toggleExpanded,
});
},
},
];
const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]);
const globalFilter = ref('');
const rowSelection = ref({});
const expanded = ref<ExpandedState>({});
function handleSortingChange(item: any) {
if (typeof item === 'function') {
const sortValue = item();
const sortBy = sortValue[0]?.id ? sortValue[0].id : '' ;
const sortDirection = sortBy ? sortValue[0]?.desc ? 'desc' : 'asc' : '';
const data: {[index: string]: any} = {};
data[sortBy] = sortDirection;
router.visit(route('permissions.index'), {
data,
only: ['permissions'],
preserveScroll: true,
preserveState: true,
onSuccess: (page) => {
console.log(page);
sorting.value = sortValue;
}
});
}
}
const table = useVueTable({
data: props.permissions.data,
columns,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
// manualSorting: true,
// enableSortingRemoval: true,
pageCount: props.permissions.per_page,
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: (updaterOrValue) => handleSortingChange(updaterOrValue),
onColumnFiltersChange: (updaterOrValue) => valueUpdater(updaterOrValue, columnFilters),
onGlobalFilterChange: (updaterOrValue) => valueUpdater(updaterOrValue, globalFilter),
onRowSelectionChange: (updaterOrValue) => valueUpdater(updaterOrValue, rowSelection),
state: {
get sorting() {
return sorting.value;
},
get columnFilters() {
return columnFilters.value;
},
get globalFilter() {
return globalFilter.value;
},
get rowSelection() {
return rowSelection.value;
},
get expanded() {
return expanded.value;
},
},
});
</script>
<template>
<AppLayout :breadcrumbs="breadcrumbs">
<Head title="Permisos" />
<IndexLayout title="Permisos">
<div class="w-full">
<div class="flex items-center py-4">
<Input
class="max-w-sm"
placeholder="Filter emails..."
:model-value="globalFilter ?? ''"
@update:model-value="(value) => (globalFilter = String(value))"
/>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline" class="ml-auto"> Columns <ChevronDown class="ml-2 h-4 w-4" /> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuCheckboxItem
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
:key="column.id"
class="capitalize"
:model-value="column.getIsVisible()"
@update:model-value="
(value: any) => {
column.toggleVisibility(!!value);
}
"
>
{{ column.id }}
</DropdownMenuCheckboxItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div class="rounded-md border">
<Table>
<TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<TableHead v-for="header in headerGroup.headers" :key="header.id">
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<template v-for="row in table.getRowModel().rows" :key="row.id">
<TableRow :data-state="row.getIsSelected() && 'selected'">
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell>
</TableRow>
<TableRow v-if="row.getIsExpanded()">
<TableCell :colspan="row.getAllCells().length">
{{ JSON.stringify(row.original) }}
</TableCell>
</TableRow>
</template>
</template>
<TableRow v-else>
<TableCell :colspan="columns.length" class="h-24 text-center"> No results. </TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<div class="flex items-center justify-end space-x-2 py-4">
<div class="flex-1 text-sm text-muted-foreground">
{{ table.getFilteredSelectedRowModel().rows.length }} de {{ table.getFilteredRowModel().rows.length }} fila(s) seleccionadas.
</div>
<div class="space-x-2">
<Button variant="outline" size="sm" :disabled="!table.getCanPreviousPage()" @click="table.previousPage()"> Anterior </Button>
<Button variant="outline" size="sm" :disabled="!table.getCanNextPage()" @click="table.nextPage()"> Siguiente </Button>
</div>
</div>
</div>
</IndexLayout>
</AppLayout>
</template>