Radio Deck plugin screenshot
Dark mode ready
No multilingual support
Supports v5.x

Radio Deck by Jay-Are Ocero

Community

Turn Filament's radio and checkbox list into selectable cards with icons, descriptions, colors, search and relationships.

Tags: Form Field
Supported versions:
5.x 4.x 3.x
Jay-Are Ocero avatar Author: Jay-Are Ocero

Package health

Automated checks of this plugin's Composer package

88 / 100
Security 85
Maintenance 88
Ecosystem 100
15 checks
  • Skipped: GitHub Actions pinned to SHA
  • Skipped: GitLab CI includes pinned to SHA
  • Passed: Open security advisories
  • Passed: Dependabot PR responsiveness — No open Dependabot PRs.
  • Skipped: Renovate MR responsiveness
  • Skipped: Dependabot or Renovate configured
  • Skipped: Dependency update cooldown configured
  • Failed: Provides a security policy — View details on Plumb
  • Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist).
  • Warning: Commit and release recency — Low activity: no commit date available; last release 206 days ago.
  • Passed: composer.lock not committed by library — composer.lock is absent from the released dist archive.
  • Passed: Dist archive is lean
  • Passed: Current Laravel version supported — Package dependencies resolve together with current Laravel 13.0.
  • Passed: Current PHP version supported — Constraint ^8.2 supports current PHP 8.5.
  • Skipped: Current Symfony version supported
Third-party plugin. This is built by the community, not the Filament team. Filament does not review, endorse, or vet the security of plugins outside the filament/ namespace. Review the source and install at your own risk. Found malware or an unresolved security issue the author won't address? Report it .
Powered by Plumb Last scanned 1 day ago

Documentation

Version:

Header

Latest Version on Packagist Total Downloads

Turn Filament's radio and checkbox list into selectable cards with icons, titles, descriptions and colors.

#Requirements

#Installation

composer require jaocero/radio-deck

Add the plugin's views to your custom theme's CSS file (usually resources/css/filament/admin/theme.css), then rebuild your assets:

@source '../../../../vendor/jaocero/radio-deck/resources/views';

Publish the plugin's JavaScript (used by searchable() and bulkToggleable()):

php artisan filament:assets

#Usage

use Filament\Schemas\Schema;
use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\IconPosition;
use Filament\Support\Enums\IconSize;
use Filament\Support\Icons\Heroicon;
use JaOcero\RadioDeck\Forms\Components\RadioDeck;

public static function configure(Schema $schema): Schema
{
    return $schema
        ->components([
            RadioDeck::make('platform')
                ->options([
                    'ios' => 'iOS',
                    'android' => 'Android',
                    'web' => 'Web',
                ])
                ->descriptions([
                    'ios' => 'iOS mobile app',
                    'android' => 'Android mobile app',
                    'web' => 'Web app',
                ])
                ->icons([
                    'ios' => Heroicon::OutlinedDevicePhoneMobile,
                    'android' => 'heroicon-o-device-phone-mobile',
                    'web' => Heroicon::OutlinedGlobeAlt,
                ])
                ->required()
                ->iconSize(IconSize::Medium) // ExtraSmall | Small | Medium | Large | ExtraLarge | TwoExtraLarge, or classes like 'size-12'
                ->iconPosition(IconPosition::Before) // Before | After
                ->alignment(Alignment::Center) // Start | Center | End
                ->optionsGap('gap-5') // Gap between the icon and the text inside a card
                ->padding('px-4 py-6') // Padding inside each card
                ->extraCardsAttributes(['class' => 'rounded-xl'])
                ->extraOptionsAttributes(['class' => 'text-lg'])
                ->extraDescriptionsAttributes(['class' => 'text-sm font-light'])
                ->color('primary') // Color of every option
                ->columns(3), // or ['sm' => 1, 'md' => 2, 'lg' => 3]
        ]);
}

#Colors

Use one color for every option, or a color per option. Colors can be names registered with Filament or palettes from Filament\Support\Colors\Color:

use Filament\Support\Colors\Color;

RadioDeck::make('plan')
    ->options([...])
    ->colors([
        'hobby' => 'gray',
        'pro' => Color::Violet,
        'team' => Color::hex('#0ea5e9'),
    ])

color() is only the fallback for options without their own color. A color from colors(), the options enum's HasColor, or getOptionColorFromRecordUsing() always wins. To force one color on every option, pass it to colors() instead:

RadioDeck::make('platform')
    ->options(Platform::class)
    ->colors('danger') // Overrides the enum's colors

#Using enums

Pass an enum to options() and RadioDeck reads labels, descriptions, icons and colors from Filament's own enum contracts. The selected value is cast to the enum.

use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasDescription;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Icons\Heroicon;

enum Platform: string implements HasColor, HasDescription, HasIcon, HasLabel
{
    case Ios = 'ios';
    case Web = 'web';

    public function getLabel(): string
    {
        return match ($this) {
            self::Ios => 'iOS',
            self::Web => 'Web',
        };
    }

    public function getDescription(): string
    {
        return match ($this) {
            self::Ios => 'iOS mobile app',
            self::Web => 'Web app',
        };
    }

    public function getIcon(): Heroicon
    {
        return match ($this) {
            self::Ios => Heroicon::OutlinedDevicePhoneMobile,
            self::Web => Heroicon::OutlinedGlobeAlt,
        };
    }

    public function getColor(): string
    {
        return match ($this) {
            self::Ios => 'gray',
            self::Web => 'info',
        };
    }
}
RadioDeck::make('platform')
    ->options(Platform::class)

Explicit descriptions(), icons() and colors() arrays still take priority over the enum.

The JaOcero\RadioDeck\Contracts\HasDescriptions and HasIcons contracts (with ->descriptions(Enum::class) and ->icons(Enum::class)) still work, but are deprecated. Implement Filament's HasDescription and HasIcon instead.

#Multiple selection

multiple() renders checkboxes and stores an array. Cast the attribute to array (or an enum collection) on your model.

RadioDeck::make('regions')
    ->options([...])
    ->multiple()
    ->minItems(1)
    ->maxItems(3)

#Searching and bulk toggling

RadioDeck::make('regions')
    ->options([...])
    ->multiple()
    ->searchable()
    ->bulkToggleable() // "Select all" and "Deselect all", multiple() only
    ->searchPrompt('Search regions')
    ->noSearchResultsMessage('No regions found.')
    ->searchDebounce(300)

Customize the bulk toggle actions with selectAllAction(fn (Action $action) => $action->label(...)) and deselectAllAction().

#Integrating with an Eloquent relationship

For a BelongsTo relationship, name the field after the foreign key:

use App\Models\Category;

RadioDeck::make('category_id')
    ->relationship('category', 'name')
    ->getOptionDescriptionFromRecordUsing(fn (Category $record): ?string => $record->description)
    ->getOptionIconFromRecordUsing(fn (Category $record): ?string => $record->icon)
    ->getOptionColorFromRecordUsing(fn (Category $record): ?string => $record->color)

For a BelongsToMany relationship, enable multiple(). The selection is synced to the pivot table, and pivotData() is saved with it:

RadioDeck::make('features')
    ->relationship(titleAttribute: 'name')
    ->multiple()
    ->pivotData(['note' => 'Added by an admin'])

relationship() also accepts a modifyQueryUsing closure, and getOptionLabelFromRecordUsing() customizes the labels.

#Boolean options

RadioDeck::make('is_public')
    ->boolean('Public', 'Private')
    ->icons([1 => Heroicon::OutlinedLockOpen, 0 => Heroicon::OutlinedLockClosed])

#Inline layout

inline() places the cards next to each other instead of in a grid:

RadioDeck::make('size')
    ->options([...])
    ->inline()

In a grid, cards fill rows first. gridDirection(GridDirection::Column) fills columns first instead.

#Tooltips

RadioDeck::make('plan')
    ->options([...])
    ->tooltips([
        'enterprise' => 'Contact sales to unlock this plan.',
    ])

#Selection indicator

Show a radio or checkbox style indicator in the corner of each card:

RadioDeck::make('plan')
    ->options([...])
    ->selectionIndicator()

The indicator follows alignment(): vertically centered by default, at the top with Alignment::Start and at the bottom with Alignment::End. Stacked cards (with a flex-col class) keep it in the top corner. With the default padding, cards reserve space on the right for the indicator. If you set your own padding(), it's used as-is, so keep it symmetric for centered tiles.

#Disabling options

RadioDeck::make('plan')
    ->options([...])
    ->disableOptionWhen(fn (string $value): bool => $value === 'enterprise')

Submitted values are validated against the enabled options automatically.

#Styling

Every part of the card has a stable CSS class you can target from your theme: fi-fo-radio-deck, fi-fo-radio-deck-option, fi-fo-radio-deck-card, fi-fo-radio-deck-icon, fi-fo-radio-deck-label, fi-fo-radio-deck-description and fi-fo-radio-deck-indicator.

#Upgrading

#From v1.x (Filament v3)

  1. Create a custom theme with php artisan make:filament-theme.
  2. Remove the plugin from the content array of your tailwind.config.js and add the @source directive shown above to your theme CSS.
  3. Rename ->gap() to ->optionsGap().

#Deprecated enum contracts

Replace JaOcero\RadioDeck\Contracts\HasDescriptions::getDescriptions() and HasIcons::getIcons() with Filament's HasDescription::getDescription() and HasIcon::getIcon(). You can then drop the ->descriptions(Enum::class) and ->icons(Enum::class) calls, because ->options(Enum::class) picks them up.

#Testing

composer test

#Changelog

Please see CHANGELOG for more information on what has changed recently.

#Contributing

Please see CONTRIBUTING for details.

#Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

#Credits

#License

The MIT License (MIT). Please see License File for more information.