Desired Feature

Using a header button, the ‘company’ filter is toggled on and off with a custom option (“Google”).

Multiple Selection Filter Example

Filter Example

/*
Company filter is applied to the 'company' field on the Company model

Table function defined in the CompanyResource.php file as created by Filament
*/
namespace App\Filament\Resources;

public static function table(Table $table): Table
{
    return $table
        ->headerActions([
            Tables\Actions\Action::make('google_filter')
                ->badge()
                ->label('Google')
                ->icon(fn (ListRecords|TableWidget $livewire) => in_array(Companies::Google->value, Arr::get($livewire->tableFilters ?? [], 'company.values', []))
                    ? 'heroicon-s-funnel'
                    : 'heroicon-o-funnel'
                )
                ->action(fn (ListRecords|TableWidget $livewire) => self::buttonFilterApply($livewire, 'company', Companies::Google->value)),
            Tables\Actions\Action::make('apple_filter')
                ->badge()
                ->label('Apple')
                ->icon(fn (ListRecords|TableWidget $livewire) => in_array(Companies::Apple->value, Arr::get($livewire->tableFilters ?? [], 'company.values', []))
                    ? 'heroicon-s-funnel'
                    : 'heroicon-o-funnel'
                )
                ->action(fn (ListRecords|TableWidget $livewire) => self::buttonFilterApply($livewire, 'company', Companies::Apple->value)),
        ])
        ->filters([
            SelectFilter::make('company')
                ->multiple()
                ->native(false)
                ->options(Companies::class),
        ])
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('job_title'),
            TextColumn::make('location'),
            TextColumn::make('company'),
        ]);
}

private static function buttonFilterApply(ListRecords|TableWidget $livewire, string $filter, string $option): void
{
    $filter_value = (array) Arr::get($livewire->tableFilters ?? [], $filter, []);
    if (empty($filter_value)) {
        return;
    }
    if (in_array($option, $filter_value['values'])) {
        $livewire->tableFilters[$filter]['values'] = array_diff($filter_value['values'], [$option]);
    } else {
        $livewire->tableFilters[$filter]['values'][] = $option;
    }
}

Single Selection Filter Example

Filter Example

namespace App\Filament\Resources;

public static function table(Table $table): Table
{
    return $table
        ->headerActions([
            Tables\Actions\Action::make('google_filter')
                ->badge()
                ->label('Google')
                ->icon(fn (ListRecords|TableWidget $livewire) => Arr::get($livewire->tableFilters ?? [], 'company.value') === Companies::Google->value
                    ? 'heroicon-s-funnel'
                    : 'heroicon-o-funnel'
                )
                ->action(fn (ListRecords|TableWidget $livewire) => self::buttonFilterApply($livewire, 'company', Companies::Google->value)),
        ])
        ->filters([
            SelectFilter::make('company')
                ->native(false)
                ->options(Companies::class),
        ])
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('job_title'),
            TextColumn::make('location'),
            TextColumn::make('company'),
        ]);
}

private static function buttonFilterApply(ListRecords|TableWidget $livewire, string $filter, string $option): void
{
    $filter_value = Arr::get($livewire->tableFilters ?? [], $filter);
    if (empty($filter_value)) {
        return;
    }
    $new_value = Arr::get($filter_value, 'value') === $option ? '' : $option;
    $livewire->tableFilters[$filter] = ['value' => $new_value];
}

Support

Companies Enum

/*
Optional enum class for additional clarity above
*/
use Filament\Support\Contracts\HasLabel;

enum Companies: string implements HasLabel
{
    case Google = 'Google';
    case Apple = 'Apple';
    case Yahoo = 'Yahoo';

    public function getLabel(): ?string
    {
        return $this->name;
    }
}

Livewire Type

In order for LaraStan to be happy, we need the correct livewire type applied to our icon() & action() methods on our header action - types that have the $tableFilters property.

That Filament class is Filament\Resources\Pages\ListRecords, however, in the event you utilize this table in a table widget you’ll need to union that type with Filament\Widgets\TableWidget, hence ListRecords|TableWidget $livewire.