@props([
'columns' => [],
'rows' => [],
'sortable' => true,
'paginated' => true,
'perPage' => 15,
'searchable' => false,
'emptyMessage' => 'No records found',
])
merge(['class' => 'bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 overflow-hidden']) }}
x-data="{
search: '',
sort: '{{ $columns[0]['key'] ?? '' }}',
dir: 'asc',
page: 1,
perPage: {{ $perPage }},
get filteredRows() {
let rows = $refs.tableBody ? JSON.parse($refs.tableData.value) : [];
if (this.search) {
const q = this.search.toLowerCase();
rows = rows.filter(r => Object.values(r).some(v => String(v).toLowerCase().includes(q)));
}
rows.sort((a, b) => {
let va = a[this.sort] ?? '', vb = b[this.sort] ?? '';
if (typeof va === 'number') return this.dir === 'asc' ? va - vb : vb - va;
return this.dir === 'asc' ? String(va).localeCompare(String(vb)) : String(vb).localeCompare(String(va));
});
return rows;
},
get pagedRows() {
const start = (this.page - 1) * this.perPage;
return this.filteredRows.slice(start, start + this.perPage);
},
get totalPages() { return Math.ceil(this.filteredRows.length / this.perPage) || 1; },
toggleSort(key) { if (this.sort === key) { this.dir = this.dir === 'asc' ? 'desc' : 'asc'; } else { this.sort = key; this.dir = 'asc'; } }
}">
{{-- Search --}}
@if($searchable)
@endif
{{-- Table --}}
@foreach($columns as $col)
{{ $col['label'] ?? $col['key'] }}
@if($sortable && ($col['sortable'] ?? true))
@endif
|
@endforeach
@foreach($columns as $col)
|
@endforeach
{{-- Empty state --}}
{{-- Pagination --}}
@if($paginated)
{{ __('Showing') }} – {{ __('of') }}
@endif