gsferro/filament-stat-plus-easy

Stat cards with a corner icon and a colored accent border for Filament v3, v4 and v5 — animated counter included, plus a matching lazy-loading skeleton.

Maintainers

Package info

github.com/gsferro/filament-stat-plus-easy

pkg:composer/gsferro/filament-stat-plus-easy

Transparency log

Fund package maintenance!

gsferro

Statistics

Installs: 131

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-01 15:22 UTC

This package is auto-updated.

Last update: 2026-08-01 15:23:51 UTC


README

filament-stat-plus-easy

Latest Version Total Downloads License

Filament Stat Plus Easy

🇧🇷 Português · 🇺🇸 English

O Stat do Filament v3, v4 e v5 com o visual que todo dashboard acaba pedindo: ícone no canto superior direito, borda de acento colorida à esquerda e contador animado — mais um esqueleto de carregamento com a mesma forma do card.

Troque Stat::make(...) por StatPlus::make(...), acrescente ->icon(...) e pronto. Nenhuma view publicada, nenhum CSS no seu tema.

use Gsferro\FilamentStatPlusEasy\Widgets\StatPlus;

protected function getStats(): array
{
    return [
        StatPlus::make('Projetos em andamento', Projeto::emAndamento()->count())
            ->icon('heroicon-o-folder-open'),

        StatPlus::make('SLA estourado', Ticket::atrasados()->count())
            ->icon('heroicon-o-exclamation-triangle')
            ->iconColor('danger'),
    ];
}

🎬 Demo

StatPlus no dashboard — ícone no canto, borda de acento e contador animado:

StatPlus no dashboard

Cores independentes — o ícone numa cor, a borda em outra (ou sem borda):

Cores do StatPlus

Esqueleto do carregamento lazy — mesma forma do card real, então nada salta na troca:

Esqueleto do StatPlus

O que o pacote entrega

Recurso O que faz
StatPlus Stat com ícone no canto, borda de acento e contador animado
HasStatPlus O mesmo acento em qualquer Stat seu, sem o contador
HasStatPlusPlaceholder Esqueleto de carregamento com a forma do card, para widget lazy
FilamentStatPlusEasyPlugin Padrões por painel (cor de ícone, borda ligada/desligada)

Compatibilidade

Filament Suportado
v3 (^3.2)
v4 (^4.0)
v5 (^5.0)

PHP 8.2+. O pacote não publica nem substitui a view nativa do Stat: ele injeta classes e CSS custom properties no elemento raiz que a view imprime nas três versões. Upgrade de Filament não pede nada daqui.

Instalação

composer require gsferro/filament-stat-plus-easy
php artisan filament:assets

filament:assets copia o CSS do pacote para public/ — é o passo que faz o visual aparecer. Rode-o também no deploy.

Registro do plugin (opcional, só para trocar padrões):

use Gsferro\FilamentStatPlusEasy\FilamentStatPlusEasyPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(FilamentStatPlusEasyPlugin::make());
}

Publicar a config (opcional):

php artisan vendor:publish --tag=filament-stat-plus-easy-config

Uso

O básico

->icon() é o gatilho: com ícone, o card ganha o quadrado no canto e a borda à esquerda, ambos na cor do stat.

StatPlus::make('Entidades', 42)
    ->icon('heroicon-o-building-library');

Sem ->icon() e sem ->accentColor(), o StatPlus renderiza exatamente como o stat nativo. É o que torna a migração incremental segura: troque a classe hoje, acrescente o ícone quando quiser.

Cores — ícone e borda são independentes

// os dois na cor do ícone
StatPlus::make('Entidades', 42)->icon('heroicon-o-building-library')->iconColor('success');

// só o ícone, sem borda
StatPlus::make('Entidades', 42)->icon('heroicon-o-building-library')->accentColor(false);

// ícone numa cor, borda em outra
StatPlus::make('SLA', 3)->icon('heroicon-o-clock')->iconColor('gray')->accentColor('danger');

// só a borda, sem ícone
StatPlus::make('Receita', 'R$ 1,2M')->accentColor('success');

Resolução da cor do ícone:

  1. ->iconColor('success') — vence sempre
  2. senão, ->color('danger') (o método nativo, que também pinta description e chart)
  3. senão, config('filament-stat-plus-easy.icon_color') (padrão: primary)

Resolução da cor da borda:

  1. ->accentColor(false) — sem borda
  2. ->accentColor('danger') — cor própria
  3. senão, a cor do ícone — e, sem ícone, não há borda

As cores são nomes de cores registradas no painel (->colors([...])), não hex. O CSS lê a escala pela variável --{cor}-500, que é o que faz o card seguir o tema e o modo escuro. Cor passada como array de 11 tons cai no padrão.

Tudo do Stat nativo continua valendo

StatPlus::make('Projetos', 128)
    ->icon('heroicon-o-folder-open')
    ->description('12% neste mês')
    ->descriptionIcon('heroicon-m-arrow-trending-up')
    ->chart([7, 12, 9, 14, 18, 22])
    ->url(ProjetoResource::getUrl())
    ->extraAttributes(['class' => 'minha-classe']);

Chart, description, url, polling, extraAttributes — nada é substituído. O que o pacote acrescenta é somado ao que você já passou (classe e style concatenam, não sobrescrevem).

Contador animado

StatPlus estende o OdometerStat do filament-odometer-easy — que vem junto como dependência —, então o valor anima sozinho:

StatPlus::make('Receita', 1250000)
    ->icon('heroicon-o-banknotes')
    ->format(['style' => 'currency', 'currency' => 'BRL'])
    ->duration(1500);

Quer o acento sem o contador? Aplique o trait na sua própria classe:

use Filament\Widgets\StatsOverviewWidget\Stat;
use Gsferro\FilamentStatPlusEasy\Concerns\HasStatPlus;

class MeuStat extends Stat
{
    use HasStatPlus;
}

Esqueleto de carregamento (widget lazy)

Widget lazy pinta um placeholder até hidratar. O padrão do Filament é um card liso — quando o widget real entra com ícone e borda, o layout mexe. O trait resolve:

use Filament\Widgets\StatsOverviewWidget;
use Gsferro\FilamentStatPlusEasy\Concerns\HasStatPlusPlaceholder;

class VisaoGeralStats extends StatsOverviewWidget
{
    use HasStatPlusPlaceholder;

    protected static bool $isLazy = true;
}

Por padrão o esqueleto desenha 4 cards, todos com acento, e não executa getStats() — o placeholder roda antes da hidratação, e chamar os stats ali executaria justamente as queries que o lazy adia.

Ajustes, todos opcionais:

// outra quantidade de cards
protected function getPlaceholderStatsCount(): int
{
    return 3;
}

// widget misto (alguns stats com acento, outros não): lê os stats reais e
// espelha card a card — custa as queries no placeholder
protected bool $placeholderReadsStats = true;

O esqueleto usa as mesmas classes do card real (fi-wi-stats-overview-stat, fi-stat-plus, fi-stat-plus-bordered), só que em cinza — é o que garante que a troca não desloque nada. E respeita o columnSpan do widget.

Configuração

Fluente, por painel

->plugin(
    FilamentStatPlusEasyPlugin::make()
        ->iconColor('info')   // cor padrão do ícone quando o stat não define
        ->accent(false)       // borda desligada por padrão neste painel
)

Ou pelo arquivo de config

// config/filament-stat-plus-easy.php
return [
    'icon_color' => 'primary',
    'accent' => true,
];

Medidas, via CSS

Tamanho do ícone, raio, deslocamento, espessura da borda e opacidade do fundo são custom properties com valor padrão. Sobrescreva onde fizer sentido — no tema (global), por painel ou num card só:

/* no seu theme.css */
.fi-wi-stats-overview-stat.fi-stat-plus {
    --stat-plus-icon-size: 3rem;
    --stat-plus-icon-radius: 9999px;   /* ícone redondo */
    --stat-plus-border-width: 6px;
}
// ou só neste card
StatPlus::make('Projetos', 12)
    ->icon('heroicon-o-folder-open')
    ->extraAttributes(['style' => '--stat-plus-border-width: 8px']);
Custom property Padrão O que controla
--stat-plus-icon-size 2.5rem Lado da caixa do ícone
--stat-plus-icon-padding 0.5rem Respiro entre a caixa e o desenho
--stat-plus-icon-offset 1.25rem Distância do canto do card
--stat-plus-icon-radius 0.625rem Arredondamento da caixa
--stat-plus-icon-opacity 12% Opacidade do fundo da caixa (claro)
--stat-plus-icon-opacity-dark 22% Opacidade do fundo da caixa (escuro)
--stat-plus-border-width 4px Espessura da borda de acento

Como funciona por baixo dos panos

  • Nada de view publicada. A view nativa do Stat já imprime $getExtraAttributeBag() no elemento raiz em v3, v4 e v5. O pacote injeta ali duas classes (fi-stat-plus, fi-stat-plus-bordered) e duas custom properties (--stat-plus-icon, --stat-plus-accent). O CSS faz o resto: o card já é relative em todas as versões, então o ícone só precisa ser posicionado.
  • CSS estático, registrado como asset. Nada de @apply, utilitário do Tailwind ou @layer — o arquivo não passa pelo build do app consumidor. Por isso o pacote dispensa qualquer edição de tema; filament:assets copia e o Filament injeta em todos os painéis.
  • Uma diferença real entre as versões: o formato das variáveis de cor. O v3 guarda canais RGB (--primary-500: 217 119 6) e o v4/v5 guardam a cor pronta (oklch(...)). Quem resolve é o PHP, escrevendo rgb(var(--primary-500)) ou var(--primary-500) conforme a versão instalada. O CSS fica igual para todo mundo.
  • O ícone mudou de markup entre as versões (.fi-wi-stats-overview-stat-icon no v3, .fi-icon dentro de .fi-wi-stats-overview-stat-label-ctn no v4/v5), então a regra cobre os dois seletores. Nenhum dos dois alcança o ícone da description.
  • Nome de cor é validado antes de entrar na string de style (o Filament não escapa valores de atributo extra): só slug [a-z0-9-] passa, o resto cai no padrão. Importa quando a cor vem de dado, como identidade visual por tenant.

Desenvolvimento

composer install
composer test        # Pest
composer analyse     # PHPStan
composer lint        # Pint

O CSS é plain CSS versionado em resources/dist/filament-stat-plus-easy.cssnão há passo de build. Editou, testou, commitou.

Os GIFs do README saem de art/demo/index.html (a página carrega o CSS real do pacote) com bash art/make-gifs.sh — Chrome headless para os quadros, ffmpeg para montar. Cada quadro é um screenshot com um ?t= diferente, então a captura é determinística. A capa da página de plugins (art/thumbnail.jpg, 1280x720) sai da mesma página com bash art/make-thumbnail.sh.

Testes

composer test

Changelog

Veja o CHANGELOG.

Contributing

Veja o CONTRIBUTING.

Security Vulnerabilities

Veja a política de segurança.

Credits

License

MIT. Veja o arquivo de licença.