/* global React */ const { useState, useEffect, useRef, useCallback, useMemo } = React; // ===================================================================== // Ícones (stroke SVG, inline) // ===================================================================== const Ico = ({ d, size = 14, className = 'ico' }) => ( ); const IcoCart = (p) => ; const IcoBox = (p) => ; const IcoClock = (p) => ; const IcoStack = (p) => ; const IcoSliders = (p) => ; const IcoMap = (p) => ; const IcoPlus = (p) => ; const IcoX = (p) => ; const IcoSave = (p) => ; const IcoDownload = (p) => ; const IcoUpload = (p) => ; const IcoRefresh = (p) => ; const IcoTrash = (p) => ; const IcoEye = (p) => ; const IcoFile = (p) => ; const IcoSparkle = (p) => ; const IcoLogout = (p) => ; window.Icons = { IcoCart, IcoBox, IcoClock, IcoStack, IcoSliders, IcoMap, IcoPlus, IcoX, IcoSave, IcoDownload, IcoUpload, IcoRefresh, IcoTrash, IcoEye, IcoFile, IcoSparkle, IcoLogout }; // ===================================================================== // Tooltip (hover sobre .tip) // ===================================================================== function Tooltip({ children, text }) { const [show, setShow] = useState(false); const [pos, setPos] = useState({ x: 0, y: 0 }); const ref = useRef(); const onEnter = (e) => { const r = e.currentTarget.getBoundingClientRect(); setPos({ x: r.left + r.width/2, y: r.top }); setShow(true); }; return ( <> setShow(false)} tabIndex={0}>? {show && (
{text}
)} ); } window.Tooltip = Tooltip; // ===================================================================== // Segmented (Sim/Não toggle moderno) // ===================================================================== function Segmented({ value, options, onChange, full = true }) { return (
{options.map(o => ( ))}
); } window.Segmented = Segmented; // ===================================================================== // Toast // ===================================================================== function ToastHost({ toast }) { if (!toast) return null; return (
{toast.kind === 'ok' && } {toast.msg}
); } window.ToastHost = ToastHost; // ===================================================================== // NumInput: input numérico tolerante (aceita vírgula, decimal incremental) // ===================================================================== function NumInput({ value, onChange, ...rest }) { const [draft, setDraft] = useState(String(value ?? '')); const [focused, setFocused] = useState(false); useEffect(() => { if (!focused) setDraft(String(value ?? '')); }, [value, focused]); return ( setFocused(true)} onBlur={() => { setFocused(false); const num = parseFloat(String(draft).replace(',', '.')); if (!isNaN(num)) onChange(num); else if (draft === '') onChange(0); else setDraft(String(value ?? '')); }} onChange={(e) => { const v = e.target.value; setDraft(v); const num = parseFloat(v.replace(',', '.')); if (!isNaN(num) && /^-?\d+([.,]\d+)?$/.test(v.trim())) onChange(num); }} {...rest} /> ); } window.NumInput = NumInput;