Providers
SpotlightSistema de providers PHP para organizar items del Spotlight. Soporta navegación, acciones, búsqueda remota y flujos multi-paso.
Clase base
Todos los providers extienden SpotlightProvider. La clase base proporciona los métodos que puedes sobreescribir.
abstract class SpotlightProvider
{
public function __construct(
protected readonly ?Component $livewireComponent = null
) {}
// Items que siempre se muestran (input vacío)
public function items(): array { return []; }
// Items filtrados por query — default devuelve items(), Alpine filtra
// Override para búsqueda server-side
public function search(string $query): array { return $this->items(); }
// Orden entre providers (menor = primero)
public function priority(): int { return 100; }
// Nombre del grupo/sección
public function group(): string { return 'General'; }
}abstract class SpotlightProvider
{
public function __construct(
protected readonly ?Component $livewireComponent = null
) {}
// Items que siempre se muestran (input vacío)
public function items(): array { return []; }
// Items filtrados por query — default devuelve items(), Alpine filtra
// Override para búsqueda server-side
public function search(string $query): array { return $this->items(); }
// Orden entre providers (menor = primero)
public function priority(): int { return 100; }
// Nombre del grupo/sección
public function group(): string { return 'General'; }
}Provider de acciones
Provider que usa el contexto del componente Livewire para mostrar acciones condicionales.
namespace App\Spotlight;
use KoreUi\Spotlight\SpotlightItem;
use KoreUi\Spotlight\SpotlightProvider;
class ActionProvider extends SpotlightProvider
{
public function group(): string { return 'Acciones'; }
public function priority(): int { return 20; }
public function items(): array
{
return [
SpotlightItem::make('Crear usuario')
->icon('user-plus')
->shortcut('⌘U')
->action('openCreateUser')
->gate('create-users'),
SpotlightItem::make('Exportar CSV')
->icon('download')
->action('exportCsv')
->when($this->livewireComponent?->hasSelection() ?? false),
];
}
}namespace App\Spotlight;
use KoreUi\Spotlight\SpotlightItem;
use KoreUi\Spotlight\SpotlightProvider;
class ActionProvider extends SpotlightProvider
{
public function group(): string { return 'Acciones'; }
public function priority(): int { return 20; }
public function items(): array
{
return [
SpotlightItem::make('Crear usuario')
->icon('user-plus')
->shortcut('⌘U')
->action('openCreateUser')
->gate('create-users'),
SpotlightItem::make('Exportar CSV')
->icon('download')
->action('exportCsv')
->when($this->livewireComponent?->hasSelection() ?? false),
];
}
}Provider de búsqueda remota
Para providers que buscan en base de datos, sobreescribe search() en lugar de items().
namespace App\Spotlight;
use App\Models\User;
use KoreUi\Spotlight\SpotlightItem;
use KoreUi\Spotlight\SpotlightProvider;
class UserSearchProvider extends SpotlightProvider
{
public function group(): string { return 'Usuarios'; }
public function priority(): int { return 50; }
public function items(): array { return []; } // vacío sin input
public function search(string $query): array
{
return User::search($query)->limit(5)->get()
->map(fn($user) => SpotlightItem::make($user->name)
->description($user->email)
->icon('user')
->route('users.show', $user)
)->all();
}
}namespace App\Spotlight;
use App\Models\User;
use KoreUi\Spotlight\SpotlightItem;
use KoreUi\Spotlight\SpotlightProvider;
class UserSearchProvider extends SpotlightProvider
{
public function group(): string { return 'Usuarios'; }
public function priority(): int { return 50; }
public function items(): array { return []; } // vacío sin input
public function search(string $query): array
{
return User::search($query)->limit(5)->get()
->map(fn($user) => SpotlightItem::make($user->name)
->description($user->email)
->icon('user')
->route('users.show', $user)
)->all();
}
}Nota: La diferencia entre items() y search(): items() se carga en el mount y Alpine filtra con fuzzy search. search() se llama con debounce por $wire -- es para consultas pesadas a base de datos.
Registro de providers
Registra tus providers en la configuración global o por instancia del componente.
'spotlight' => [
'providers' => [
\App\Spotlight\NavigationProvider::class,
\App\Spotlight\ActionProvider::class,
\App\Spotlight\UserSearchProvider::class,
],
],'spotlight' => [
'providers' => [
\App\Spotlight\NavigationProvider::class,
\App\Spotlight\ActionProvider::class,
\App\Spotlight\UserSearchProvider::class,
],
],<x-kore::spotlight :providers="[\App\Spotlight\NavigationProvider::class]" /><x-kore::spotlight :providers="[\App\Spotlight\NavigationProvider::class]" />Prioridad y grupos
Controla el orden y la organización visual de los items.
priority()determina el orden en que aparecen los grupos (menor numero = arriba).group()del provider se usa como grupo para los items que no definen su propio grupo.- Si un
SpotlightItemdefine->group('Custom'), ese grupo se respeta sobre el del provider.
SpotlightItem API
Builder fluido para definir items del Spotlight. El ID se genera con Str::slug($name) para estabilidad entre sesiones.
SpotlightItem::make('Nombre del item')
// Contenido
->description('Descripción opcional')
->icon('user-plus') // Nombre de blade-lucide-icons
->group('Acciones') // Sección en la lista
->shortcut('⌘U') // Badge visual de atajo
// Acciones (mutuamente excluyentes)
->route('users.create') // Navegar a ruta nombrada
->route('users.show', ['id' => 1]) // Con parámetros
->url('https://example.com') // URL externa (misma pestaña)
->url('https://example.com', true) // URL externa (nueva pestaña)
->action('exportCsv') // Llamar método Livewire
->action('createUser', ['role' => 'admin']) // Con params
->dispatch('kore:open', ['name' => 'overlays.my-modal']) // Evento Alpine/Livewire
// Búsqueda
->synonym('crear', 'nuevo', 'agregar') // Términos extra para fuzzy
// Visibilidad
->hidden() // Solo aparece en búsqueda (no en lista vacía)
->when($condition) // Solo si la condición es true
->gate('create-users') // Solo si el usuario pasa el gate
// Multi-paso
->dependency(SpotlightDependency::search(...))
->dependency(SpotlightDependency::input(...))
->dependency(SpotlightDependency::select(...));SpotlightItem::make('Nombre del item')
// Contenido
->description('Descripción opcional')
->icon('user-plus') // Nombre de blade-lucide-icons
->group('Acciones') // Sección en la lista
->shortcut('⌘U') // Badge visual de atajo
// Acciones (mutuamente excluyentes)
->route('users.create') // Navegar a ruta nombrada
->route('users.show', ['id' => 1]) // Con parámetros
->url('https://example.com') // URL externa (misma pestaña)
->url('https://example.com', true) // URL externa (nueva pestaña)
->action('exportCsv') // Llamar método Livewire
->action('createUser', ['role' => 'admin']) // Con params
->dispatch('kore:open', ['name' => 'overlays.my-modal']) // Evento Alpine/Livewire
// Búsqueda
->synonym('crear', 'nuevo', 'agregar') // Términos extra para fuzzy
// Visibilidad
->hidden() // Solo aparece en búsqueda (no en lista vacía)
->when($condition) // Solo si la condición es true
->gate('create-users') // Solo si el usuario pasa el gate
// Multi-paso
->dependency(SpotlightDependency::search(...))
->dependency(SpotlightDependency::input(...))
->dependency(SpotlightDependency::select(...));Tipos de acción
Cada item puede tener un tipo de acción. Son mutuamente excluyentes.
| Método | Comportamiento |
|---|---|
->route('name', $params) | Navega via Livewire redirect |
->url($url) | window.location.href = $url |
->url($url, true) | window.open($url, '_blank') |
->action('method', $params) | $wire.call('method', ...params) |
->dispatch('event', $data) | $dispatch('event', data) |
Visibilidad y autorización
Controla qué items son visibles según condiciones y permisos.
// Solo incluir si hay filas seleccionadas (contexto del componente)
SpotlightItem::make('Exportar selección')
->when($this->livewireComponent?->hasSelection() ?? false)
// Solo si el usuario tiene el permiso
SpotlightItem::make('Crear usuario')
->gate('create-users')
// Combinado
SpotlightItem::make('Eliminar')
->when(!$isReadOnly)
->gate('delete-records')// Solo incluir si hay filas seleccionadas (contexto del componente)
SpotlightItem::make('Exportar selección')
->when($this->livewireComponent?->hasSelection() ?? false)
// Solo si el usuario tiene el permiso
SpotlightItem::make('Crear usuario')
->gate('create-users')
// Combinado
SpotlightItem::make('Eliminar')
->when(!$isReadOnly)
->gate('delete-records')Los items filtrados por when() o gate() nunca llegan al frontend (filtrado en SpotlightProvider::toArray()).
Dependencias multi-paso
Las dependencias permiten flujos multi-paso: seleccionar un item puede requerir inputs previos antes de ejecutar la acción final. Cada paso completado aparece como una 'pill' en el input.
Paso 0: Usuario busca "Asignar rol"
┌──────────────────────────────────────┐
│ Asignar rol a usuario > │
└──────────────────────────────────────┘
↓ selecciona el item
Paso 1: SpotlightDependency::search
┌───────────────────────────────────────────────────┐
│ [Asignar rol a usuario x] Seleccionar usuario… │
├───────────────────────────────────────────────────┤
│ Juan García │
│ Ana López │
└───────────────────────────────────────────────────┘
↓ selecciona Juan García
Paso 2: SpotlightDependency::select
┌──────────────────────────────────────────────────────────┐
│ [Asignar rol x] [Juan García x] Seleccionar rol… │
├──────────────────────────────────────────────────────────┤
│ Administrador │
│ Editor │
│ Viewer │
└──────────────────────────────────────────────────────────┘
↓ selecciona Editor
Ejecuta: $wire.call('assignRole', ['juan-garcia', 'editor'])Paso 0: Usuario busca "Asignar rol"
┌──────────────────────────────────────┐
│ Asignar rol a usuario > │
└──────────────────────────────────────┘
↓ selecciona el item
Paso 1: SpotlightDependency::search
┌───────────────────────────────────────────────────┐
│ [Asignar rol a usuario x] Seleccionar usuario… │
├───────────────────────────────────────────────────┤
│ Juan García │
│ Ana López │
└───────────────────────────────────────────────────┘
↓ selecciona Juan García
Paso 2: SpotlightDependency::select
┌──────────────────────────────────────────────────────────┐
│ [Asignar rol x] [Juan García x] Seleccionar rol… │
├──────────────────────────────────────────────────────────┤
│ Administrador │
│ Editor │
│ Viewer │
└──────────────────────────────────────────────────────────┘
↓ selecciona Editor
Ejecuta: $wire.call('assignRole', ['juan-garcia', 'editor'])El usuario puede presionar Backspace con el input vacío para eliminar la última pill y volver al paso anterior.
Tipos de dependencia
Tres tipos de dependencia disponibles para flujos multi-paso.
SpotlightDependency::search(
placeholder: 'Seleccionar usuario',
searchUrl: 'users.spotlight', // ruta nombrada o URL
method: 'GET' // opcional, default: GET
)SpotlightDependency::search(
placeholder: 'Seleccionar usuario',
searchUrl: 'users.spotlight', // ruta nombrada o URL
method: 'GET' // opcional, default: GET
)SpotlightDependency::input(
placeholder: 'Motivo de la acción',
validation: 'min:3' // opcional, validación básica
)SpotlightDependency::input(
placeholder: 'Motivo de la acción',
validation: 'min:3' // opcional, validación básica
)SpotlightDependency::select(
placeholder: 'Seleccionar rol',
options: [
'admin' => 'Administrador',
'editor' => 'Editor',
'viewer' => 'Viewer',
]
)SpotlightDependency::select(
placeholder: 'Seleccionar rol',
options: [
'admin' => 'Administrador',
'editor' => 'Editor',
'viewer' => 'Viewer',
]
)Ejemplo multi-paso
Ejemplo completo de un flujo de 2 pasos con su método Livewire receptor.
SpotlightItem::make('Asignar rol a usuario')
->icon('shield')
->group('Acciones')
->action('assignRole') // se ejecuta con los valores resueltos
->dependency(
SpotlightDependency::search('Seleccionar usuario', 'users.spotlight')
)
->dependency(
SpotlightDependency::select('Seleccionar rol', [
'admin' => 'Administrador',
'editor' => 'Editor',
'viewer' => 'Viewer',
])
);SpotlightItem::make('Asignar rol a usuario')
->icon('shield')
->group('Acciones')
->action('assignRole') // se ejecuta con los valores resueltos
->dependency(
SpotlightDependency::search('Seleccionar usuario', 'users.spotlight')
)
->dependency(
SpotlightDependency::select('Seleccionar rol', [
'admin' => 'Administrador',
'editor' => 'Editor',
'viewer' => 'Viewer',
])
);// Los valores resueltos se pasan como argumentos al método
public function assignRole(string $userId, string $role): void
{
User::find($userId)->assignRole($role);
$this->toast()->success('Rol asignado')->send();
}// Los valores resueltos se pasan como argumentos al método
public function assignRole(string $userId, string $role): void
{
User::find($userId)->assignRole($role);
$this->toast()->success('Rol asignado')->send();
}Endpoint para búsqueda
El endpoint para dependencias de tipo search debe devolver un array de items compatibles.
[
{
"id": "user-1",
"name": "Juan García",
"description": "juan@empresa.com",
"icon": "user"
}
][
{
"id": "user-1",
"name": "Juan García",
"description": "juan@empresa.com",
"icon": "user"
}
]// routes/web.php
Route::get('/spotlight/users', function (Request $request) {
return User::search($request->query)->limit(10)->get()
->map(fn($u) => [
'id' => (string) $u->id,
'name' => $u->name,
'description' => $u->email,
'icon' => 'user',
]);
});// routes/web.php
Route::get('/spotlight/users', function (Request $request) {
return User::search($request->query)->limit(10)->get()
->map(fn($u) => [
'id' => (string) $u->id,
'name' => $u->name,
'description' => $u->email,
'icon' => 'user',
]);
});