Teren-app/resources/js/Components/DataTable/README.md
2025-11-20 18:11:43 +01:00

391 lines
9.4 KiB
Markdown

# DataTable Component - Usage Guide
This DataTable component follows the shadcn-vue architecture and uses TanStack Table v8 for powerful table functionality.
## Features
- ✅ Client-side and server-side pagination
- ✅ Sorting (single column)
- ✅ Filtering/Search
- ✅ Row selection
- ✅ Column visibility toggle
- ✅ Customizable column definitions
- ✅ Loading states
- ✅ Empty states
- ✅ Flexible toolbar
- ✅ Cell-level customization via slots
- ✅ Responsive design
- ✅ Laravel Inertia integration
## Basic Usage
### Simple Format (Recommended for basic tables)
```vue
<script setup>
import DataTable from '@/Components/DataTable/DataTableNew2.vue';
const columns = [
{ key: 'id', label: 'ID', sortable: true },
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email', sortable: true },
{ key: 'status', label: 'Status' },
];
const data = ref([
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' },
]);
</script>
<template>
<DataTable :columns="columns" :data="data" />
</template>
```
### Advanced Format (Full TanStack Table power)
```vue
<script setup>
import { h } from 'vue';
import DataTable from '@/Components/DataTable/DataTableNew2.vue';
import { Badge } from '@/Components/ui/badge';
import { columns } from './columns'; // Import from separate file
const data = ref([...]);
</script>
<template>
<DataTable :columns="columns" :data="data" />
</template>
```
See `columns-example.js` for comprehensive column definition examples.
## Props
### Data Props
- `columns` (Array, required) - Column definitions (simple or TanStack format)
- `data` (Array, default: []) - Array of data objects
- `meta` (Object, default: null) - Laravel pagination meta for server-side
- `loading` (Boolean, default: false) - Loading state
### Server-side Props
- `routeName` (String) - Laravel route name for server-side requests
- `routeParams` (Object) - Additional route parameters
- `pageParamName` (String, default: 'page') - Custom page parameter name
- `onlyProps` (Array) - Inertia.js only props
- `preserveState` (Boolean, default: true)
- `preserveScroll` (Boolean, default: true)
### Sorting & Filtering
- `sort` (Object, default: {key: null, direction: null})
- `search` (String, default: '')
- `filterColumn` (String) - Column to filter on
- `filterPlaceholder` (String, default: 'Filter...')
### Pagination
- `showPagination` (Boolean, default: true)
- `pageSize` (Number, default: 10)
- `pageSizeOptions` (Array, default: [10, 25, 50, 100])
### Features
- `enableRowSelection` (Boolean, default: false)
- `showToolbar` (Boolean, default: true)
- `striped` (Boolean, default: false)
- `hoverable` (Boolean, default: true)
- `rowKey` (String|Function, default: 'id')
### Empty State
- `emptyText` (String, default: 'No results.')
- `emptyIcon` (String|Object|Array)
- `emptyDescription` (String)
## Events
- `@update:search` - Emitted when search changes
- `@update:sort` - Emitted when sort changes
- `@update:page` - Emitted when page changes
- `@update:pageSize` - Emitted when page size changes
- `@row:click` - Emitted when row is clicked
- `@selection:change` - Emitted when selection changes
## Client-side Example
```vue
<script setup>
import DataTable from '@/Components/DataTable/DataTableNew2.vue';
const columns = [
{ key: 'id', label: 'ID', sortable: true },
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email', sortable: true },
];
const data = ref([
// Your data here
]);
</script>
<template>
<DataTable
:columns="columns"
:data="data"
:page-size="10"
filter-column="email"
filter-placeholder="Filter emails..."
enable-row-selection
/>
</template>
```
## Server-side Example (Laravel Inertia)
### Controller
```php
public function index(Request $request)
{
$query = Client::query();
// Search
if ($request->search) {
$query->where('name', 'like', "%{$request->search}%")
->orWhere('email', 'like', "%{$request->search}%");
}
// Sort
if ($request->sort && $request->direction) {
$query->orderBy($request->sort, $request->direction);
}
$clients = $query->paginate($request->per_page ?? 10);
return Inertia::render('Clients/Index', [
'clients' => $clients,
'filters' => $request->only(['search', 'sort', 'direction']),
]);
}
```
### Vue Component
```vue
<script setup>
import DataTable from '@/Components/DataTable/DataTableNew2.vue';
const props = defineProps({
clients: Object,
filters: Object,
});
const columns = [
{ key: 'id', label: 'ID', sortable: true },
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email', sortable: true },
];
</script>
<template>
<DataTable
:columns="columns"
:data="clients.data"
:meta="clients.meta"
:search="filters.search"
:sort="{ key: filters.sort, direction: filters.direction }"
route-name="clients.index"
filter-column="email"
filter-placeholder="Search clients..."
:only-props="['clients']"
/>
</template>
```
## Custom Cell Rendering
### Using Slots
```vue
<template>
<DataTable :columns="columns" :data="data">
<!-- Custom cell for status column -->
<template #cell-status="{ value, row }">
<Badge :variant="value === 'active' ? 'default' : 'secondary'">
{{ value }}
</Badge>
</template>
<!-- Custom cell for actions -->
<template #cell-actions="{ row }">
<Button @click="editRow(row)">Edit</Button>
</template>
</DataTable>
</template>
```
### Using Column Definitions
```javascript
import { h } from 'vue';
import { Badge } from '@/Components/ui/badge';
export const columns = [
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => {
const status = row.getValue('status');
return h(Badge, {
variant: status === 'active' ? 'default' : 'secondary'
}, () => status);
},
},
];
```
## Custom Toolbar
The new toolbar is simplified and follows shadcn-vue patterns:
```vue
<template>
<DataTable
:columns="columns"
:data="data"
filter-column="email"
filter-placeholder="Search emails..."
>
<!-- Add custom filter controls -->
<template #toolbar-filters="{ table }">
<select
@change="table.getColumn('status')?.setFilterValue($event.target.value)"
class="h-8 rounded-md border px-3"
>
<option value="">All Status</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
</template>
<!-- Add custom action buttons -->
<template #toolbar-actions="{ table }">
<Button @click="exportData">Export</Button>
<Button @click="addNew">Add New</Button>
</template>
</DataTable>
</template>
```
Or completely replace the toolbar:
```vue
<template>
<DataTable :columns="columns" :data="data" :show-toolbar="false">
<template #toolbar="{ table }">
<div class="flex items-center justify-between mb-4">
<Input
:model-value="table.getColumn('email')?.getFilterValue()"
@update:model-value="table.getColumn('email')?.setFilterValue($event)"
placeholder="Filter emails..."
class="max-w-sm"
/>
<div class="flex gap-2">
<Button @click="exportData">Export</Button>
<DataTableViewOptions :table="table" />
</div>
</div>
</template>
</DataTable>
</template>
```
## Row Selection
```vue
<script setup>
import { ref } from 'vue';
import DataTable from '@/Components/DataTable/DataTableNew2.vue';
const selectedRows = ref([]);
function handleSelectionChange(keys) {
selectedRows.value = keys;
console.log('Selected rows:', keys);
}
</script>
<template>
<DataTable
:columns="columns"
:data="data"
enable-row-selection
@selection:change="handleSelectionChange"
/>
<div v-if="selectedRows.length">
Selected {{ selectedRows.length }} row(s)
</div>
</template>
```
## Row Click Handler
```vue
<script setup>
function handleRowClick(row, index) {
console.log('Clicked row:', row);
// Navigate or perform action
router.visit(route('clients.show', row.id));
}
</script>
<template>
<DataTable
:columns="columns"
:data="data"
@row:click="handleRowClick"
/>
</template>
```
## Tips
1. **Column Keys**: Always use consistent keys/accessorKeys across your data
2. **Server-side**: Always provide `meta` and `routeName` props together
3. **Performance**: For large datasets, use server-side pagination
4. **Styling**: Use column `class` property for custom styling
5. **Slots**: Prefer slots for complex cell rendering over h() functions
## Migration from Old DataTable
### Before (Old API)
```vue
<DataTable
:rows="clients.data"
:columns="columns"
:meta="clients.meta"
/>
```
### After (New API)
```vue
<DataTableNew2
:data="clients.data"
:columns="columns"
:meta="clients.meta"
route-name="clients.index"
/>
```
Main changes:
- `rows``data`
- Added `route-name` prop for server-side
- More consistent prop naming
- Better TypeScript support
- More flexible column definitions
## Component Files
- `DataTableNew2.vue` - Main table component
- `DataTableColumnHeader.vue` - Sortable column header
- `DataTablePagination.vue` - Pagination controls
- `DataTableViewOptions.vue` - Column visibility toggle
- `DataTableToolbar.vue` - Toolbar component
- `columns-example.js` - Column definition examples