K
KoreUI

Avanzado

DataTable

Filter presets, inline editing, column select, responsive modes, slots y configuración global.

Demo en vivo

DataTable con filter presets, inline editing, column select y modo responsive card.

Por página
Progreso
AirPods Max
Audio $549.00 28
active
100%
AirPods Pro 3
Audio $249.00 40
draft
55%
AirTag 4-Pack
Accesorios $99.00 100
active
100%
Apple Pencil Pro
Accesorios $129.00 60
active
100%
Apple TV 4K
Streaming $129.00 35
active
100%
Apple Watch Ultra
Wearables $899.00 15
active
72%
Beats Solo 4
Audio $199.00 30
active
100%
HomePod mini
Audio $99.00 72
active
100%
Mac Mini M4
Desktops $699.00 20
draft
45%
Mac Pro M4 Ultra
Desktops $6,999.00 2
archived
100%
Mac Studio M4 Ultra
Desktops $3,999.00 6
draft
25%
MacBook Air 13"
Laptops $1,099.00 38
active
90%
MacBook Pro 14"
Laptops $1,999.00 22
active
85%
MacBook Pro 16"
Laptops $2,499.00 12
active
100%
MagSafe Charger
Accesorios $39.00 200
active
100%
Magic Keyboard
Accesorios $299.00 50
active
100%
Magic Mouse
Accesorios $99.00 45
active
100%
Pro Display XDR
Monitores $4,999.00 3
archived
100%
Studio Display
Monitores $1,599.00 8
active
60%
Vision Pro
Wearables $3,499.00 5
active
30%
iPad Air M2
Tablets $799.00 33
active
88%
iPad Pro 13" M4
Tablets $1,299.00 18
active
70%
iPad mini 7
Tablets $499.00 25
active
65%
iPhone 15 Pro
Smartphones $1,199.00 45
active
95%
iPhone SE 4
Smartphones $429.00 55
active
80%
Total: $34,405.00 Productos: 25

Filter Presets

Tabs predefinidos que aplican combinaciones de filtros, sorts y búsqueda.

Definir presets
use KoreUi\DataTable\Presets\FilterPreset;

public function filterPresets(): array
{
    return [
        FilterPreset::make('all', 'Todos')
            ->icon('layers')
            ->default(),

        FilterPreset::make('active', 'Activos')
            ->icon('check-circle')
            ->filters(['status' => 'active'])
            ->count(fn () => Product::where('status', 'active')->count()),

        FilterPreset::make('draft', 'Borradores')
            ->icon('pencil')
            ->filters(['status' => 'draft'])
            ->sorts(['updated_at' => 'desc']),
    ];
}
use KoreUi\DataTable\Presets\FilterPreset;

public function filterPresets(): array
{
    return [
        FilterPreset::make('all', 'Todos')
            ->icon('layers')
            ->default(),

        FilterPreset::make('active', 'Activos')
            ->icon('check-circle')
            ->filters(['status' => 'active'])
            ->count(fn () => Product::where('status', 'active')->count()),

        FilterPreset::make('draft', 'Borradores')
            ->icon('pencil')
            ->filters(['status' => 'draft'])
            ->sorts(['updated_at' => 'desc']),
    ];
}

Inline Editing

Edita celdas directamente en la tabla con validación.

Columnas editables
Column::make('Nombre', 'name')
    ->editable()
    ->editableRules(['required', 'min:3', 'max:255'])

// BadgeColumn con select editable
BadgeColumn::make('Estado', 'status')
    ->editable()
    ->editableOptions([
        'active'   => 'Activo',
        'draft'    => 'Borrador',
        'archived' => 'Archivado',
    ])

// BooleanColumn con toggle
BooleanColumn::make('Activo', 'is_active')
    ->editable()  // Auto-toggle al click
Column::make('Nombre', 'name')
    ->editable()
    ->editableRules(['required', 'min:3', 'max:255'])

// BadgeColumn con select editable
BadgeColumn::make('Estado', 'status')
    ->editable()
    ->editableOptions([
        'active'   => 'Activo',
        'draft'    => 'Borrador',
        'archived' => 'Archivado',
    ])

// BooleanColumn con toggle
BooleanColumn::make('Activo', 'is_active')
    ->editable()  // Auto-toggle al click

Column Select

Toggle de visibilidad de columnas con persistencia en sesión.

Configurar Column Select
public function configure(): void
{
    $this->setColumnSelectEnabled();  // Activa el toggle
}

// Desactivar globalmente en config/kore-ui.php
'datatable' => [
    'column_select' => false,
]
public function configure(): void
{
    $this->setColumnSelectEnabled();  // Activa el toggle
}

// Desactivar globalmente en config/kore-ui.php
'datatable' => [
    'column_select' => false,
]

Responsive

Tres modos de visualización responsive.

Configurar modo responsive
public function configure(): void
{
    // scroll (default) — Scroll horizontal
    // card             — Cada fila como card vertical
    // collapse         — Columnas colapsables con expand
    $this->setResponsiveMode('card');
    $this->setResponsiveBreakpoint(640); // default: 768
}

// Marcar columnas colapsables (modo collapse)
Column::make('Email', 'email')->collapseOnMobile();
Column::make('Ciudad', 'city')->collapseOnTablet();
public function configure(): void
{
    // scroll (default) — Scroll horizontal
    // card             — Cada fila como card vertical
    // collapse         — Columnas colapsables con expand
    $this->setResponsiveMode('card');
    $this->setResponsiveBreakpoint(640); // default: 768
}

// Marcar columnas colapsables (modo collapse)
Column::make('Email', 'email')->collapseOnMobile();
Column::make('Ciudad', 'city')->collapseOnTablet();

Sorting avanzado

Sort multi-columna con tiebreaker y campos personalizados.

Sort avanzado
public function configure(): void
{
    // Sort con tiebreaker
    $this->setDefaultSort('status', 'desc')
         ->addDefaultSort('id', 'asc');
    // Genera: ORDER BY status DESC, id ASC
}

// Sort con campo personalizado (útil en joins)
Column::make('Nombre completo', 'name')
    ->sortableAs('users.name')
public function configure(): void
{
    // Sort con tiebreaker
    $this->setDefaultSort('status', 'desc')
         ->addDefaultSort('id', 'asc');
    // Genera: ORDER BY status DESC, id ASC
}

// Sort con campo personalizado (útil en joins)
Column::make('Nombre completo', 'name')
    ->sortableAs('users.name')

Búsqueda avanzada

Búsqueda en relaciones y callbacks personalizados.

Búsqueda avanzada
// Búsqueda en relaciones (dot notation)
// Genera: whereHas('company', fn($q) => $q->where('name', 'like', '%term%'))
Column::make('Empresa', 'company.name')->searchable(),

// Búsqueda personalizada
Column::make('Nombre', 'name')
    ->searchCallback(function (Builder $query, string $term) {
        $query->where('first_name', 'like', "%{$term}%")
              ->orWhere('last_name', 'like', "%{$term}%");
    }),
// Búsqueda en relaciones (dot notation)
// Genera: whereHas('company', fn($q) => $q->where('name', 'like', '%term%'))
Column::make('Empresa', 'company.name')->searchable(),

// Búsqueda personalizada
Column::make('Nombre', 'name')
    ->searchCallback(function (Builder $query, string $term) {
        $query->where('first_name', 'like', "%{$term}%")
              ->orWhere('last_name', 'like', "%{$term}%");
    }),

Paginación

Tres tipos de paginación según tus necesidades.

Tipos de paginación
public function configure(): void
{
    $this->setPaginationType('standard'); // Con total y números
    $this->setPaginationType('simple');   // Solo anterior/siguiente
    $this->setPaginationType('cursor');   // Eficiente para datasets grandes
}
public function configure(): void
{
    $this->setPaginationType('standard'); // Con total y números
    $this->setPaginationType('simple');   // Solo anterior/siguiente
    $this->setPaginationType('cursor');   // Eficiente para datasets grandes
}

Slots

Inyecta vistas Blade en puntos del layout.

Configurar slots
public function configure(): void
{
    $this->setSlot('before-toolbar', 'partials.table-header');
    $this->setSlot('toolbar-right-end', 'partials.table-actions', [
        'createRoute' => route('products.create'),
    ]);
}
public function configure(): void
{
    $this->setSlot('before-toolbar', 'partials.table-header');
    $this->setSlot('toolbar-right-end', 'partials.table-actions', [
        'createRoute' => route('products.create'),
    ]);
}
Área Posición
before-toolbarEncima del toolbar
after-toolbarDebajo del toolbar
toolbar-right-endExtremo derecho del toolbar
after-tableDespués de la paginación

Ejemplo completo

DataTable con todas las funcionalidades combinadas.

app/Livewire/ProductsTable.php
<?php

namespace App\Livewire;

use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use KoreUi\DataTable\Actions\BulkAction;
use KoreUi\DataTable\Actions\RowAction;
use KoreUi\DataTable\Columns\ActionColumn;
use KoreUi\DataTable\Columns\BadgeColumn;
use KoreUi\DataTable\Columns\Column;
use KoreUi\DataTable\Columns\DateColumn;
use KoreUi\DataTable\Columns\NumberColumn;
use KoreUi\DataTable\Filters\SelectFilter;
use KoreUi\DataTable\KoreDataTable;
use KoreUi\DataTable\Presets\FilterPreset;

class ProductsTable extends KoreDataTable
{
    public function configure(): void
    {
        $this->setDefaultSort('created_at', 'desc');
        $this->setFilterLayout('slide-down');
        $this->setColumnSelectEnabled();
        $this->setResponsiveMode('card');
    }

    public function query(): Builder
    {
        return Product::query();
    }

    public function columns(): array
    {
        return [
            Column::make('Producto', 'name')
                ->sortable()
                ->searchable()
                ->editable()
                ->editableRules(['required', 'min:3']),

            NumberColumn::make('Precio', 'price')
                ->sortable()
                ->money('USD', 'en_US')
                ->sum('Total'),

            BadgeColumn::make('Estado', 'status')
                ->sortable()
                ->colors([
                    'active'   => 'success',
                    'inactive' => 'muted',
                    'draft'    => 'warning',
                ])
                ->editable()
                ->editableOptions([
                    'active'   => 'Activo',
                    'inactive' => 'Inactivo',
                    'draft'    => 'Borrador',
                ]),

            DateColumn::make('Creado', 'created_at')
                ->sortable()
                ->dateFormat('d M Y')
                ->collapseOnMobile(),

            ActionColumn::make()
                ->actions([
                    RowAction::make('edit', 'Editar')
                        ->icon('pencil')
                        ->openOverlay('edit-product', fn ($row) => ['id' => $row->id]),
                    RowAction::make('delete', 'Eliminar')
                        ->icon('trash')
                        ->color('destructive')
                        ->wireMethod('deleteProduct')
                        ->confirm('¿Eliminar este producto?')
                        ->separator(),
                ]),
        ];
    }

    public function filters(): array
    {
        return [
            SelectFilter::make('Estado', 'status')
                ->options([
                    ['label' => 'Activo', 'value' => 'active'],
                    ['label' => 'Borrador', 'value' => 'draft'],
                ])
                ->optionLabel('label')
                ->optionValue('value')
                ->placeholder('Todos'),
        ];
    }

    public function bulkActions(): array
    {
        return [
            BulkAction::make('delete', 'Eliminar seleccionados')
                ->icon('trash')
                ->color('destructive')
                ->confirm('¿Eliminar :count producto(s)?'),
        ];
    }

    public function filterPresets(): array
    {
        return [
            FilterPreset::make('all', 'Todos')->default(),
            FilterPreset::make('active', 'Activos')
                ->filters(['status' => 'active']),
        ];
    }
}
<?php

namespace App\Livewire;

use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use KoreUi\DataTable\Actions\BulkAction;
use KoreUi\DataTable\Actions\RowAction;
use KoreUi\DataTable\Columns\ActionColumn;
use KoreUi\DataTable\Columns\BadgeColumn;
use KoreUi\DataTable\Columns\Column;
use KoreUi\DataTable\Columns\DateColumn;
use KoreUi\DataTable\Columns\NumberColumn;
use KoreUi\DataTable\Filters\SelectFilter;
use KoreUi\DataTable\KoreDataTable;
use KoreUi\DataTable\Presets\FilterPreset;

class ProductsTable extends KoreDataTable
{
    public function configure(): void
    {
        $this->setDefaultSort('created_at', 'desc');
        $this->setFilterLayout('slide-down');
        $this->setColumnSelectEnabled();
        $this->setResponsiveMode('card');
    }

    public function query(): Builder
    {
        return Product::query();
    }

    public function columns(): array
    {
        return [
            Column::make('Producto', 'name')
                ->sortable()
                ->searchable()
                ->editable()
                ->editableRules(['required', 'min:3']),

            NumberColumn::make('Precio', 'price')
                ->sortable()
                ->money('USD', 'en_US')
                ->sum('Total'),

            BadgeColumn::make('Estado', 'status')
                ->sortable()
                ->colors([
                    'active'   => 'success',
                    'inactive' => 'muted',
                    'draft'    => 'warning',
                ])
                ->editable()
                ->editableOptions([
                    'active'   => 'Activo',
                    'inactive' => 'Inactivo',
                    'draft'    => 'Borrador',
                ]),

            DateColumn::make('Creado', 'created_at')
                ->sortable()
                ->dateFormat('d M Y')
                ->collapseOnMobile(),

            ActionColumn::make()
                ->actions([
                    RowAction::make('edit', 'Editar')
                        ->icon('pencil')
                        ->openOverlay('edit-product', fn ($row) => ['id' => $row->id]),
                    RowAction::make('delete', 'Eliminar')
                        ->icon('trash')
                        ->color('destructive')
                        ->wireMethod('deleteProduct')
                        ->confirm('¿Eliminar este producto?')
                        ->separator(),
                ]),
        ];
    }

    public function filters(): array
    {
        return [
            SelectFilter::make('Estado', 'status')
                ->options([
                    ['label' => 'Activo', 'value' => 'active'],
                    ['label' => 'Borrador', 'value' => 'draft'],
                ])
                ->optionLabel('label')
                ->optionValue('value')
                ->placeholder('Todos'),
        ];
    }

    public function bulkActions(): array
    {
        return [
            BulkAction::make('delete', 'Eliminar seleccionados')
                ->icon('trash')
                ->color('destructive')
                ->confirm('¿Eliminar :count producto(s)?'),
        ];
    }

    public function filterPresets(): array
    {
        return [
            FilterPreset::make('all', 'Todos')->default(),
            FilterPreset::make('active', 'Activos')
                ->filters(['status' => 'active']),
        ];
    }
}

Configuración global

Defaults globales en config/kore-ui.php.

config/kore-ui.php
'datatable' => [
    'per_page'              => 25,
    'per_page_options'      => [10, 25, 50, 100],
    'density'               => 'normal',
    'pagination_type'       => 'standard',
    'search_debounce'       => 300,
    'filter_layout'         => 'popover',
    'column_select'         => true,
    'responsive_mode'       => 'scroll',
    'responsive_breakpoint' => 768,
    'empty_text'            => 'No se encontraron resultados',
    'empty_icon'            => 'inbox',
    'translations'          => [
        'search'     => 'Buscar...',
        'per_page'   => 'Por página',
        'showing'    => 'Mostrando :from a :to de :total resultados',
        'no_results' => 'No se encontraron resultados',
    ],
],
'datatable' => [
    'per_page'              => 25,
    'per_page_options'      => [10, 25, 50, 100],
    'density'               => 'normal',
    'pagination_type'       => 'standard',
    'search_debounce'       => 300,
    'filter_layout'         => 'popover',
    'column_select'         => true,
    'responsive_mode'       => 'scroll',
    'responsive_breakpoint' => 768,
    'empty_text'            => 'No se encontraron resultados',
    'empty_icon'            => 'inbox',
    'translations'          => [
        'search'     => 'Buscar...',
        'per_page'   => 'Por página',
        'showing'    => 'Mostrando :from a :to de :total resultados',
        'no_results' => 'No se encontraron resultados',
    ],
],