// Componentes compartilhados — popovers, menus, toasts. // Drop-in para botões de Exportar, Mais opções, etc. em qualquer tela. const { useState: usePopState, useEffect: usePopEffect, useRef: usePopRef } = React; /* ──────────────────────────────────────────────────────────────── useOutsideClose — fecha o popover ao clicar fora ou apertar ESC ──────────────────────────────────────────────────────────────── */ function useOutsideClose(open, onClose) { usePopEffect(() => { if (!open) return; const onClick = () => onClose(); const onKey = (e) => { if (e.key === 'Escape') onClose(); }; // delay so the click that opened the popover doesn't immediately close it const t = setTimeout(() => window.addEventListener('click', onClick), 0); window.addEventListener('keydown', onKey); return () => { clearTimeout(t); window.removeEventListener('click', onClick); window.removeEventListener('keydown', onKey); }; }, [open, onClose]); } /* ──────────────────────────────────────────────────────────────── icon-btn com popover de exportação (PDF / CSV / XLS / email) ──────────────────────────────────────────────────────────────── */ function ExportButton({ count, label = 'registros', email = 'leticia.andrade@ezza.com.br', size = 'icon' }) { const [open, setOpen] = usePopState(false); useOutsideClose(open, () => setOpen(false)); const toast = useToast(); const handle = (fmt, msg) => { setOpen(false); toast(msg); }; return (
e.stopPropagation()}> {size === 'mini' ? ( ) : size === 'primary' ? ( ) : ( )} {open && (
e.stopPropagation()}>
Exportar {count != null && {count} {label}}
)}
); } /* ──────────────────────────────────────────────────────────────── Menu contextual de 3 pontinhos ──────────────────────────────────────────────────────────────── */ function MoreMenu({ items, size = 'mini', align = 'right', placement = 'bottom', title = 'Mais opções' }) { const [open, setOpen] = usePopState(false); useOutsideClose(open, () => setOpen(false)); const toast = useToast(); const fire = (item) => { setOpen(false); if (item.onClick) item.onClick(); else if (item.toast) toast(item.toast); }; return (
e.stopPropagation()}> {size === 'icon' ? ( ) : ( )} {open && (
e.stopPropagation()}> {items.map((it, i) => { if (it.divider) return
; if (it.section) return
{it.section}
; return ( ); })}
)}
); } /* ──────────────────────────────────────────────────────────────── Substitui o CardActions decorativo (+, upload, cal) por ações reais ──────────────────────────────────────────────────────────────── */ function CardActionsBar({ count, onCreate, onPeriod, createLabel = 'Criar', moreItems }) { const toast = useToast(); return (
{moreItems && }
); } /* ──────────────────────────────────────────────────────────────── useToast — hook + ToastHost para feedback rápido ──────────────────────────────────────────────────────────────── */ const ToastContext = React.createContext(() => {}); function ToastProvider({ children }) { const [toasts, setToasts] = usePopState([]); const push = React.useCallback((msg, opts = {}) => { const id = Date.now() + Math.random(); setToasts(t => [...t, { id, msg, kind: opts.kind || 'ok', icon: opts.icon }]); setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), opts.duration || 2400); }, []); return ( {children}
{toasts.map(t => (
{t.icon || (t.kind === 'err' ? ICN.x : ICN.check)} {t.msg}
))}
); } function useToast() { return React.useContext(ToastContext); } /* ──────────────────────────────────────────────────────────────── — substituto universal pros mini-btn decorativos Aceita uma ação ou um toast string. ──────────────────────────────────────────────────────────────── */ function ActionBtn({ icon, title, onClick, toast: toastMsg, size = 'mini', danger }) { const toast = useToast(); const cls = size === 'icon' ? 'icon-btn' : 'mini-btn'; return ( ); } /* ──────────────────────────────────────────────────────────────── Estilos compartilhados — injetados uma vez via ──────────────────────────────────────────────────────────────── */ function PopoverStyles() { return ( ); } Object.assign(window, { ExportButton, MoreMenu, CardActionsBar, ActionBtn, ToastProvider, useToast, PopoverStyles });