Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [arm092]
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to `livewire-datatables` will be documented in this file

## 3.2.4 ( 2026-09-26 )

### What's Changed

- Reset select filter controls after applying a value, removing a value, or clearing all filters.
- Use the Apricode orange accent for the filter panel so active filter badges remain distinct.
- Restyle table actions as clear, icon-led outline buttons with inverted brand colors on hover.
- Add GitHub Sponsors discovery through the README and repository funding metadata.
- Keep the security regression suite compatible with the HTTP exception handling introduced in Livewire 4.4.

## 3.2.3 ( 2026-08-11 )

### What's Changed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Build searchable, sortable, filterable and editable admin tables with Laravel, Livewire and Tailwind CSS.

[![Sponsor arm092](https://img.shields.io/badge/Sponsor-arm092-ea4aaa?style=for-the-badge&logo=githubsponsors)](https://github.com/sponsors/arm092)

![Livewire Datatables with the default Apricode theme](resources/images/screenshot.png)

[![Latest Version](https://img.shields.io/packagist/v/arm092/livewire-datatables.svg?style=for-the-badge)](https://packagist.org/packages/arm092/livewire-datatables)
Expand Down
3 changes: 3 additions & 0 deletions resources/views/icons/columns.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<svg {{ $attributes->merge(['class' => 'h-5 w-5 stroke-current']) }} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5h16v14H4zM9.333 5v14m5.334-14v14" />
</svg>
13 changes: 8 additions & 5 deletions resources/views/livewire/datatables/datatable.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@
<x-icons.cog wire:loading class="text-gray-400 h-9 w-9 animate-spin" />

@if($this->activeFilters)
<button wire:click="clearAllFilters" class="ld-button ld-button--danger flex items-center px-3 text-xs font-medium tracking-wider text-red-500 uppercase bg-white border border-red-400 space-x-2 rounded-md leading-4 hover:bg-red-200 focus:outline-none"><span>{{ __('Reset') }}</span>
<x-icons.x-circle class="m-2" />
<button wire:click="clearAllFilters" class="ld-button ld-button--danger flex items-center px-3 text-xs font-medium tracking-wider uppercase space-x-2 leading-4 focus:outline-none">
<x-icons.x-circle class="h-4 w-4" />
<span>{{ __('Reset filters') }}</span>
</button>
@endif

Expand Down Expand Up @@ -67,8 +68,10 @@ class="ld-button ld-button--success flex items-center px-4 py-2 text-xs font-med
<div x-data="{ init() {
$wire.on('startDownload', link => window.open(link, '_blank'))
} }" x-init="init">
<button wire:click="export" class="ld-button ld-button--success flex items-center px-3 text-xs font-medium tracking-wider text-green-500 uppercase bg-white border border-green-400 space-x-2 rounded-md leading-4 hover:bg-green-200 focus:outline-none"><span>{{ __('Export') }}</span>
<x-icons.excel class="m-2" /></button>
<button wire:click="export" class="ld-button ld-button--success flex items-center px-3 text-xs font-medium tracking-wider uppercase space-x-2 leading-4 focus:outline-none">
<x-icons.excel class="h-4 w-4" />
<span>{{ __('Export') }}</span>
</button>
</div>
@endif

Expand Down Expand Up @@ -137,7 +140,7 @@ class="ld-button ld-button--success px-3 py-2 text-xs font-medium tracking-wider
<div class="ld-filter-cell table-cell overflow-hidden align-top">
@if($column['filterable'])
@if( is_iterable($column['filterable']) )
<div wire:key="{{ $index }}">
<div wire:key="select-filter-{{ $index }}-{{ count($this->activeSelectFilters[$index] ?? []) }}">
@include('datatables::filters.select', ['index' => $index, 'name' => $column['label'], 'options' => $column['filterable']])
</div>
@else
Expand Down
6 changes: 2 additions & 4 deletions resources/views/livewire/datatables/filters/select.blade.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<div x-data class="ld-filter-control flex flex-col">
<div class="ld-filter-control flex flex-col">
<div class="flex">
<select
x-ref="select"
name="{{ $name }}"
class="w-full m-1 text-sm leading-4 block rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
wire:input="doSelectFilter('{{ $index }}', $event.target.value)"
x-on:input="$refs.select.value=''"
>
<option value=""></option>
@foreach($options as $value => $label)
Expand All @@ -24,7 +22,7 @@ class="w-full m-1 text-sm leading-4 block rounded-md border-gray-300 shadow-sm f

<div class="flex flex-wrap max-w-48 space-x-1">
@foreach($this->activeSelectFilters[$index] ?? [] as $key => $value)
<button wire:click="removeSelectFilter('{{ $index }}', '{{ $key }}')" x-on:click="$refs.select.value=''"
<button wire:click="removeSelectFilter('{{ $index }}', '{{ $key }}')"
class="m-1 pl-1 flex items-center uppercase tracking-wide bg-gray-300 text-white hover:bg-red-600 rounded-full focus:outline-none text-xs space-x-1">
<span>{{ $this->getDisplayValue($index, $value) }}</span>
<x-icons.x-circle />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div x-data="{ show: false }" class="ld-column-picker flex flex-col items-center">
<div class="flex flex-col items-center relative">
<button x-on:click="show = !show" class="px-3 py-2 border border-blue-400 rounded-md bg-white text-blue-500 text-xs leading-4 font-medium uppercase tracking-wider hover:bg-blue-200 focus:outline-none">
<button x-on:click="show = !show" class="ld-button ld-button--primary flex items-center px-3 py-2 space-x-2 text-xs leading-4 font-medium uppercase tracking-wider focus:outline-none">
<x-icons.columns class="h-4 w-4" />
<div class="flex items-center h-5">
{{ __('Show / Hide Columns')}}
</div>
Expand Down
34 changes: 25 additions & 9 deletions resources/views/livewire/datatables/theme.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,40 @@

.ld-table .ld-button:hover {
border-color: var(--ld-primary) !important;
background: rgb(253 151 31 / 10%) !important;
background: var(--ld-primary) !important;
color: var(--ld-ink) !important;
}

.ld-table .ld-button--primary {
border-color: var(--ld-primary) !important;
color: var(--ld-primary) !important;
}

.ld-table .ld-button--primary:hover {
background: var(--ld-primary) !important;
color: var(--ld-white) !important;
}

.ld-table .ld-button--success {
border-color: var(--ld-success) !important;
color: var(--ld-success) !important;
}

.ld-table .ld-button--success:hover {
border-color: var(--ld-success) !important;
background: rgb(166 226 46 / 14%) !important;
background: var(--ld-success) !important;
color: var(--ld-white) !important;
}

.ld-table .ld-button--danger {
border-color: rgb(249 38 114 / 40%) !important;
color: #bb1d56 !important;
border-color: var(--ld-danger) !important;
color: var(--ld-danger) !important;
}

.ld-table .ld-button--danger:hover {
border-color: var(--ld-danger) !important;
background: rgb(249 38 114 / 10%) !important;
color: #951744 !important;
background: var(--ld-danger) !important;
color: var(--ld-white) !important;
}

.ld-table .ld-panel {
Expand Down Expand Up @@ -144,8 +160,8 @@

.ld-table .ld-filter-row,
.ld-table .ld-filter-cell {
background: var(--ld-paper) !important;
border-color: var(--ld-border) !important;
background: var(--ld-primary) !important;
border-color: #d37b12 !important;
}

.ld-table .ld-data-row > .table-cell {
Expand Down Expand Up @@ -218,7 +234,7 @@
}

.ld-table .ld-filter-active {
color: #985b13 !important;
color: var(--ld-primary) !important;
}

.ld-table .ld-button.bg-blue-500 {
Expand Down
1 change: 1 addition & 0 deletions src/LivewireDatatablesServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function boot(): void
Blade::component('icons::trash', 'icons.trash');
Blade::component('icons::copy', 'icons.copy');
Blade::component('icons::excel', 'icons.excel');
Blade::component('icons::columns', 'icons.columns');
Blade::component('icons::x-circle', 'icons.x-circle');
Blade::component('icons::check-circle', 'icons.check-circle');

Expand Down
25 changes: 17 additions & 8 deletions tests/LivewireDatatableSecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ public function it_updates_only_configured_editable_columns_inside_the_builder_s

$this->assertSame('Updated', $allowed->fresh()->subject);

$this->expectException(ModelNotFoundException::class);

Livewire::test(SecureDummyTable::class)
->call('edited', 'Not allowed', 1, $excluded->getKey());
$this->assertModelNotFound(function () use ($excluded) {
return Livewire::test(SecureDummyTable::class)
->call('edited', 'Not allowed', 1, $excluded->getKey());
});
}

#[Test]
public function it_deletes_only_records_inside_the_builder_scope(): void
{
$excluded = factory(DummyModel::class)->create(['category' => 'excluded']);

$this->expectException(ModelNotFoundException::class);

Livewire::test(SecureDummyTable::class)
->call('delete', $excluded->getKey());
$this->assertModelNotFound(function () use ($excluded) {
return Livewire::test(SecureDummyTable::class)
->call('delete', $excluded->getKey());
});
}

#[Test]
Expand Down Expand Up @@ -103,4 +103,13 @@ public function it_exports_formula_like_strings_as_text(): void
$this->assertSame(DataType::TYPE_STRING, $cell->getDataType());
$this->assertSame('=HYPERLINK("https://example.test")', $cell->getValue());
}

private function assertModelNotFound(callable $callback): void
{
try {
$callback()->assertStatus(404);
} catch (ModelNotFoundException) {
$this->addToAssertionCount(1);
}
}
}
Loading