¿Qué hace Paleta de Comandos?
Reglas
- Usar la biblioteca
cmdk — componible, accesible, búsqueda difusa rápida incorporada
- Disparador:
Cmd+K (Mac) / Ctrl+K (Windows/Linux) — registrar un oyente de teclado global
- Agrupar acciones: Navegación (páginas), Comandos (acciones), Recientes (usados por última vez) — mostrar encabezados de grupo
- Navegación por teclado: teclas de flecha para moverse, Enter para seleccionar, Escape para cerrar — todo manejado por cmdk
- Búsqueda difusa: cmdk filtra automáticamente — resaltado de coincidencias con clasificación basada en puntuación
- Elementos recientes: almacenar los últimos 5-10 elementos seleccionados en localStorage — mostrar al principio cuando la entrada está vacía
- Acciones dinámicas: registrar acciones desde diferentes partes de la aplicación — conscientes del contexto (mostrar acciones diferentes en distintas páginas)
- Estado de carga: resultados de búsqueda asíncronos (ej. búsqueda API) — mostrar indicador de carga en el área de resultados
Patrones
import { Command } from "cmdk";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
function CommandPalette() {
const [open, setOpen] = useState(false);
const router = useRouter();
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((prev) => !prev);
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
return (
<Command.Dialog open={open} onOpenChange={setOpen} label="Paleta de comandos">
<Command.Input placeholder="Buscar o escribir un comando..." />
<Command.List>
<Command.Empty>No se encontraron resultados.</Command.Empty>
<Command.Group heading="Recientes">
{recentItems.map((item) => (
<Command.Item key={item.id} onSelect={() => { router.push(item.href); setOpen(false); }}>
{item.label}
</Command.Item>
))}
</Command.Group>
<Command.Group heading="Navegación">
<Command.Item onSelect={() => { router.push("/dashboard"); setOpen(false); }}>
Panel principal
</Command.Item>
<Command.Item onSelect={() => { router.push("/settings"); setOpen(false); }}>
Configuración
</Command.Item>
</Command.Group>
<Command.Group heading="Comandos">
<Command.Item onSelect={() => { toggleTheme(); setOpen(false); }}>
Alternar modo oscuro
</Command.Item>
<Command.Item onSelect={() => { logout(); setOpen(false); }}>
Cerrar sesión
</Command.Item>
</Command.Group>
</Command.List>
</Command.Dialog>
);
}
Evitar
- Construir búsqueda difusa personalizada — cmdk maneja el filtrado, la puntuación y la clasificación de fábrica
- Olvidar
e.preventDefault() en Cmd+K — sin ello, se abre la búsqueda/barra de direcciones predeterminada del navegador
- Omitir el manejo de la tecla Escape — cmdk lo maneja mediante
onOpenChange, pero asegúrate de que el foco vuelva al elemento anterior
- Acciones solo estáticas — registrar acciones dinámicamente según el contexto de la página (ej. "Editar esta publicación" solo en páginas de publicación)
- Sin estado vacío — mostrar siempre "No se encontraron resultados" cuando la búsqueda no coincida con nada