/* 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;