// ActionBar — barra de acciones de un documento (benchmark §5.4 / §6.2): // una primaria (verbo + sustantivo), 2–3 secundarias visibles, el resto en // «Más…» y las destructivas separadas (divisor) y en rojo. Una acción bloqueada se ve // deshabilitada con su motivo (no desaparece: el operador entiende por qué). import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Loader2, MoreHorizontal } from 'lucide-react' import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@asteby/metacore-ui/primitives' import { cn } from '@asteby/metacore-ui/lib' import { DynamicIcon } from '../dynamic-icon' import type { ResolvedAction, ResolvedActionLayout } from './types' export interface ActionBarProps { layout: ResolvedActionLayout /** * Ejecuta la acción. Si devuelve una promesa, el botón muestra `loading` * hasta que se resuelve (acciones asíncronas, §6.2). */ onAction: (action: ResolvedAction) => void | Promise className?: string } export function ActionBar({ layout, onAction, className }: ActionBarProps) { const { t } = useTranslation() const [busy, setBusy] = useState(null) const run = async (a: ResolvedAction) => { if (a.blockedReason || busy) return setBusy(a.def.key) try { await onAction(a) } finally { setBusy(null) } } const label = (a: ResolvedAction) => t(a.def.label, { defaultValue: a.def.label }) const { primary, secondary, more, destructive } = layout const hasMenu = more.length > 0 const button = (a: ResolvedAction, variant: 'default' | 'outline' | 'destructive') => { const loading = busy === a.def.key const btn = ( ) if (!a.blockedReason) return btn return ( {/* span: un botón disabled no dispara el tooltip */} {btn} {t(a.blockedReason, { defaultValue: a.blockedReason })} ) } const item = (a: ResolvedAction) => ( run(a)} > {a.def.icon ? ( ) : null} {label(a)} {a.blockedReason ? ( {t(a.blockedReason, { defaultValue: a.blockedReason })} ) : null} ) return (
{primary ? button(primary, primary.def.destructive ? 'destructive' : 'default') : null} {secondary.map((a) => button(a, 'outline'))} {hasMenu ? ( {more.map((a) => item(a))} ) : null} {destructive.length > 0 ? ( <> {destructive.map((a) => button(a, 'destructive'))} ) : null}
) }