Saltar al contenido principal

Diseño

El problema con los diseños de tabla tradicionales

Las tablas tradicionales son conocidas por tener mala capacidad de respuesta. En móvil, hay un límite en la flexibilidad al renderizar contenido que es horizontalmente largo:

  • Permitir al usuario desplazarse horizontalmente para ver más contenido de la tabla
  • Ocultar columnas no importantes en dispositivos pequeños

Ambas son posibles con Filament. Las tablas automáticamente se desplazan horizontalmente cuando desbordan, y puedes elegir mostrar u ocultar columnas según el punto de quiebre del navegador. Para esto, puedes usar los métodos visibleFrom() o hiddenFrom():

use Filament\Tables\Columns\TextColumn;

TextColumn::make('slug')
->visibleFrom('md')

Esto está bien, pero aún hay un problema evidente: en móvil, el usuario no puede ver mucha información de una fila de tabla a la vez sin desplazarse.

Afortunadamente, Filament te permite construir interfaces de tabla responsivas, sin tocar HTML o CSS. Estos diseños te permiten definir exactamente dónde aparece el contenido, en cada punto de quiebre.

Permitir que las columnas se apilen en móvil

Introduzcamos un componente: Split:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
TextColumn::make('email'),
])

El componente Split envuelve columnas y les permite apilarse en móvil.

Por defecto, las columnas dentro de un split aparecen siempre lado a lado. Sin embargo, puedes elegir un punto de quiebre donde este comportamiento comience con from(). Antes de ese punto, las columnas se apilarán:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
TextColumn::make('email'),
])->from('md')

Evitar que una columna genere espacios en blanco

Como las columnas de tabla, los splits ajustan su espacio automáticamente. Puedes evitarlo usando grow(false):

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular()
->grow(false),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
TextColumn::make('email'),
])

Las demás columnas que usen grow() se expandirán al espacio liberado.

Apilar dentro de un split

Dentro de un split, puedes apilar varias columnas verticalmente:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
Stack::make([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
]),
])

Ocultar stack en móvil

Puedes ocultar un stack según el punto de quiebre:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
Stack::make([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])->visibleFrom('md'),
])

Alinear contenido apilado

Por defecto, las columnas de un stack están alineadas al inicio. Puedes alinearlas al Alignment::Center o Alignment::End:

use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
Stack::make([
TextColumn::make('phone')
->icon('heroicon-m-phone')
->grow(false),
TextColumn::make('email')
->icon('heroicon-m-envelope')
->grow(false),
])
->alignment(Alignment::End)
->visibleFrom('md'),
])

Añadir espacio en contenido apilado

Puedes usar space(1|2|3):

use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Stack::make([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])->space(1)

Controlar ancho de columnas con Grid

Grid permite usar CSS Grid en lugar de Flexbox:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\TextColumn;

Grid::make([
'lg' => 2,
])
->schema([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])

Puedes personalizar número de columnas por breakpoint:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Grid::make([
'lg' => 2,
'2xl' => 4,
])
->schema([
Stack::make([
TextColumn::make('name'),
TextColumn::make('job'),
]),
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])

E incluso especificar columnSpan:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Grid::make([
'lg' => 2,
'2xl' => 5,
])
->schema([
Stack::make([
TextColumn::make('name'),
TextColumn::make('job'),
])->columnSpan([
'lg' => 'full',
'2xl' => 2,
]),
TextColumn::make('phone')
->icon('heroicon-m-phone')
->columnSpan([
'2xl' => 2,
]),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])

Contenido colapsable

Puedes usar Split, Stack, o el componente Panel con collapsible():

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Panel;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
]),
Panel::make([
Stack::make([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
]),
])->collapsible(),
]

Expandir por defecto con collapsed(false):

use Filament\Tables\Columns\Layout\Panel;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\TextColumn;

Panel::make([
Split::make([
TextColumn::make('phone')
->icon('heroicon-m-phone'),
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])->from('md'),
])->collapsed(false)

Organizar registros en grid

El método $table->contentGrid():

use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Table;

public function table(Table $table): Table
{
return $table
->columns([
Stack::make([
// Columns
]),
])
->contentGrid([
'md' => 2,
'xl' => 3,
]);
}

HTML personalizado

Puedes incluir HTML usando View:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\View;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
]),
View::make('users.table.collapsible-row-content')
->collapsible(),
]

Archivo Blade:

<p class="px-4 py-3 bg-gray-100 rounded-lg">
<span class="font-medium">
Email address:
</span>

<span>
{{ $getRecord()->email }}
</span>
</p>

Incrustar otros componentes

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\View;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
Split::make([
ImageColumn::make('avatar')
->circular(),
TextColumn::make('name')
->weight(FontWeight::Bold)
->searchable()
->sortable(),
]),
View::make('users.table.collapsible-row-content')
->components([
TextColumn::make('email')
->icon('heroicon-m-envelope'),
])
->collapsible(),
]

Blade:

<div class="px-4 py-3 bg-gray-100 rounded-lg">
@foreach ($getComponents() as $layoutComponent)
{{ $layoutComponent
->record($getRecord())
->recordKey($getRecordKey())
->rowLoop($getRowLoop())
->renderInLayout() }}
@endforeach
</div>